How do I unit-test Maven multi-module Spring app? - java

I'm having problems trying to unit test a maven multi-module project with Spring.
I've 4 modules:
application-core
application-data
application-service
application-web
This is my test, its in the application-core module:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:/config/application-context.xml")
public class TicketTest {
#Mock
ITicketDAO ticketDAO;
#Autowired
#InjectMocks
ITicketCore ticketCore;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testRegisterTicket_Ticket_NotUsed_isValid() {
Long ticketNumber = 0L;
when(ticketDAO.getTicket(anyLong())).thenReturn(null);
final boolean isValidTicket = ticketCore.validateTicket(ticketNumber);
assertTrue(isValidTicket);
}
}
And here is the implementation:
#Component
#Scope("prototype")
public class TicketCore implements ITicketCore{
private ITicketDAO ticketDao;
#Autowired
public TicketCore(ITicketDAO ticketDao) {
this.ticketDao = ticketDao;
}
#Override
public boolean validateTicket(Long ticketNumber) {
ITicket ticket = ticketDao.getTicket(ticketNumber);
return ticket != null;
}
}
Interface:
public interface ITicketDAO {
ITicket getTicket(Long ticketNumber);
}
Implementation of ITicketDAO its on the application-data module:
#Service
public class TicketDAO implements ITicketDAO {
#Override
public ITicket getTicket(Long ticketNumber) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}
I'm having problems testing this code because the context doesn't find the implementation of ITicketDAO. It's seems obvious, because when the test are running, JUnit doesn't care about putting in the classpath the "others modules".
Spring throws BeanCreationException for obvious reasons.
Am I right?
So I would like to test my project without Spring getting in the tests' way.
What could I do to get my tests run w/o any problem???
I've created dummy classes in the test folder/packages, and it works, but...
I would eventually have ALL the external implementations in my application-core module's test folder.
There is a better way?
Thanks in advance.
UPDATE:
application-data
application-service
application-web
all of them depends on application-core. Spring successfully inject TicketCore(application-core). What I want is to give Spring "something" (a dummy class) to inject in ITicketDAO just to run the test.
<beans>
<context:component-scan base-package="ve.gov.imat.transimat" />
<context:annotation-config />
<aop:config proxy-target-class="true" />
</beans>

Pretend that each Maven module is a completely separate project. Put tests specifically of each module's code inside it, and add integration tests in the module where all of the dependencies necessary to run them have been included.
You haven't provided any information on the dependencies between your modules, but it appears that the problem you're running into is that you need some implementation of an interface for testing purposes, but your production bean is defined in another module. This is what mocking frameworks like EasyMock and Mockito are for; they allow you to write simple placeholder implementations so that you can test TicketCore specifically, and it's a good idea to use them even when the real implementation is available so that you can be sure you're just testing one component at a time.
In TicketTest, you're correctly defining your Mockito mock for ITicketDAO, but your TicketCore is still trying to autoacquire the bean from Spring, even though you haven't registered it. Either manually register your bean into the test context, or place the definition for the mock in an #Configuration in src/test.

If I've understood you well your problem is your context file references a class you don't have available in your test classpath.
In principle tests shouldn't need the implementation of any collaborator to work, only the one of the sut.
One solution is to create an application-test-context.xml file under your test/resources folder to be used in your test instead of the production one. Within this file you can create the mocks of your collaborators
<!-- Mock service for splitting jobs -->
<bean factory-bean="mockControl" factory-method="createMock"
primary="true">
<constructor-arg value="net.compart.docpilot.model.service.JobSplitService" />
</bean>

Related

Spring Boot - how to initialize class from a dependent module with injected dependencies

I have two Maven projects A and B, where project B is nested in project A. The structure looks like the following:
Project A:
src/test/java:
MyTest.java
Project B:
src/test/java:
MyNewTest.java
pom.xml
pom.xml
My goal is to let MyNewTest.java act as a wrapper of MyTest.java, and be able to invoke the test methods declared in MyTest.java from MyNewTest.java. MyTest has some injected dependencies.
My question is: how to initialize MyTest in MyNewTest to make sure that all the dependencies of MyTest are injected properly?
MyTest looks like the following:
public class MyTest {
#Autowired
Service service;
#Autowired
TestUtil util;
Info info;
#PostConstruct
void init() {
info = service.getStuff();
}
#Test
public test1() {
service.getMoreStuff();
// more code omitted
}
}
I have tried adding #Component to MyTest, and then in MyNewTest.java, use #Autowired like the following:
public class MyNewTest {
#Autowired
private MyTest baseTest;
#Test
public void runTest() {
// run the test in MyTest.java
baseTest.test1();
}
}
But this doesn't seem to work - baseTest is null. I also tried initializing MyTest by calling its default constructor, but the dependencies failed to be injected as expected. Can anyone suggest a proper way to initialize MyTest.java so that all its dependencies are injected as well? Thanks
The problem with your test is the #Autowired annotations as the Spring wiring isn't being triggered. There are two different paths you can take to fix this.
The first option is to manually instantiate baseTest in MyNewTest at which point it will no longer be null. However, tests will still fail because your two #Autowired dependencies will be null. You can either add setters or inject them via your constructor. Note- these classes should be mocked if you are performing Unit Tests. Here's how it would look if you chose to add these classes via the constructor -
private Service service;
private TestUtil util;
private MyTest baseTest;
#BeforeEach
void setUp() {
service = mock(Service.class);
util = mock(TestUtil.class);
baseTest = new MyTest(service, util);
}
The second option is to add configuration to your Test class to support the Spring wiring. I am not familiar with taking this route as I always choose the first option when possible. There are multiple ways to add the Spring wiring but the easiest is-
#SpringBootTest
#RunWith(SpringRunner.class)
public class MyNewTest {
However, this does not cover all use cases so you may need more specific configurations. You can find some of the possible options here - How to write JUnit test with Spring Autowire?
Edit- As I immediately recognized a problem, I didn't read the rest very carefully and I missed that these were Test classes you were trying to wire together. I am not sure if this is possible.
Only Spring beans can be #Autowired so the first step would be to attempt to add configuration to make your Test class into a Spring bean. I've never heard of this being done before but it would be easy to try. If not, you can get around this problem by using the first option.
The second problem is that tests are not included in the artifact. I'd imagine you could circumvent this issue by mixing your Test classes in with your regular classes but this is considered a bad practice. I've never heard of tests being dependent on other tests but I'd guess this is also a bad practice. What's the reason for wanting to create your Tests this way?

Dagger2: How to use #Inject in a JUnit test?

I would like to have the ability to inject dependencies into JUnit tests with Dagger 2 (I'm new to this framework). Coming from Spring, there you can do something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApplication.class)
public class MyTestClass {
#Autowired
private MyService service;
#Test
public void testMySerivce() { /* ... */ }
}
... but with Dagger 2 I have not yet found a solution that does not rely on an explicit DaggerMyComponent.builder().build().myService().
Ideally, I would imagine the solution to look something like the following:
// tell JUnit that dagger needs to do some post processing
#RunWith(DaggerJUnit4Runner.class)
// tell dagger which component classes to use for injection
#Components(MyComponent.class)
public class MyTestClass {
#Inject
private MyService service;
#Test
public void testMySerivce() { /* ... */ }
}
Unfortunately, there is no DaggerJunit4Runner.
Any hints on how this could be accomplished would be greatly appreciated.
I haven't seen any built-in feature for this, or any prominent testing library that supports this.
Dagger does all its dependency wiring at compile time, and only in ways you tell it to; unlike Spring, there's no code written to read a testing class at runtime or supply the dependencies it needs. Dagger's appeal over Guice and Spring comes from compile-time compilation, validation, and optimization. Though what you describe would be very useful, that kind of reflection is antithetical to Dagger's original motivations. Either your Dagger component is compiled with the generated code to inject the test, or you need to be able to pull those dependencies out independently as you've listed above.
For unit tests you'll probably need to skip Dagger and create your classes or their mocks manually; for system or integration tests, you'll need to expose all of the classes you need on the component definition. If you want to replace dependencies with test doubles for repeatability or isolation, you'll need to make your component sufficiently configurable to accept the replacement implementations, or you'll need to create a new for-testing Component implementation that uses the test doubles instead of the real implementations.

How can I make a unit test that tests my Spring/OSGi configuration?

I have a bundle-context-osgi.xml file that lists services my project takes and services it publishes. I also have a bundle-context.xml that defines the beans defined in my application. How can I write a unit test that ensures that everything is wired correctly and I can product the services that my application is supposed to provide when it is on the server?
Note: it might be wrong to call this a "unit test" but I think the idea is clear.
For OSGi Integration Tests I usually use Pax Exam. It will start a osgi container publish all configured bundles and will create a tiny-bundle from the testsources to be deployed as a OSGi bundle.
With it you are able to access all registered services even by #Inject means.
Best to take a look at the Pax Exam documentation.
In short you create a new integration test module, where you configure your dependencies in the Test case:
#RunWith(PaxExam.class)
#ExamReactorStrategy(PerMethod.class)
public class MyTest {
#Inject
protected BundleContext bundleContext;
#Inject
MyService service;
...
#Configuration
public Option[] configure() {
return options(
workingDirectory("target/paxexam/"),
cleanCaches(true),
junitBundles(),
mavenBundle().groupId("my.group.id")
.artifactId("my-artifact")
.version("1.0.0")
...
);
}
...
#Test
public void test() throws Exception {
assertNotNull(service);
}
}

Spring & JUnit: Spring instantiates two instances of test class, only injects dependencies for one, runs the other one

What I'm trying to do: Run a single JUnit test (one JUnit test class with a single test method). Test class has one dependency that I expect Spring to handle (inject) prior to running the test.
What I see: Test class is instantiated twice, dependency is satisfied (injected) for one of the instances but not the other. Test is run using the instance for which dependencies are not injected.
My guess: I'm speculating that the test class is instantiated twice because Spring is using the first instance to analyze dependencies (probably using reflection to look for annotations, which I'm not using).
But: I cannot for my life figure out why the test class instance for which the dependency is not created/injected is used to run the test.
test.xml
<beans ...>
<bean id="test" class="com.example.MyTestClass">
<property name="primaryDependency" ref="pridep"/>
</bean>
<bean id="pridep" class="com.example.MyPrimaryDependency">
<property name="dataSource" ref="ds"/>
</bean>
<bean id="ds" class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"/>
</beans>
MyTestClass.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:test.xml"})
public class MyTestClass {
private IPrimaryDependency mDep = null;
public MyTestClass() {
}
public void setPrimaryDependency(IPrimaryDependency dep) {
mDep = dep;
}
#Before
public void createImageData() {
}
#Test
public void testImageSearch() {
assertNotNull("mDep is null", mDep);
}
}
MyPrimaryDependency.java
public class MyPrimaryDependency extends RequiresADataSource implements IPrimaryDependency {
public MyPrimaryDependency() {
}
}
Existing related question: Spring dependency injection null when running a test case, but I'm not able to discern what's going on from that answer.
Sorry for the rookie question-- I've been scouring documentation (and bothering colleagues) for several days and am baffled by this.
Thanks!
Add #Autowired
#Autowired
private IPrimaryDependency mDep;
Explanation:
JUnit creates brand new instances of test classes for each test.
Spring JUnit runner only extends this behaviour by "preparing" created objects which basically means setting up application context and injecting annotation driven dependencies.
In your case the test class is instantiated twice: From test.xml (the property is injected there) and by JUnit runner to run the first test method.
However when created by JUnit, Spring has no idea that it needs to inject something because there are no annotations in the class thus the property ends up being null.
See source code of SpringJUnit4ClassRunner and DependencyInjectionTestExecutionListener for implementation details.
Your test class is
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:test.xml"})
public class MyTestClass {
You run this class with JUnit. Because of #RunWith, JUnit uses an instance of SpringJUnit4ClassRunner to instantiate the class and run its tests.
Because of #ContextConfiguration, the ClassRunner will generate an ApplicationContext by loading your test.xml file.
That test.xml file has a <bean> definition for your test class
<bean id="test" class="com.example.MyTestClass">
<property name="primaryDependency" ref="pridep"/>
</bean>
So Spring will also generate an instance and set the appropriate property.
Basically, what I'm getting at is that your test class shouldn't be a bean in the Spring context. Create a different test class and inject the bean that has an injection target and inject that into your test class.

How to access Spring #Service object from jUnit test

Situation: I have service implementation class annotated with #Service with access to properties file.
#Service("myService")
public class MySystemServiceImpl implements SystemService{
#Resource
private Properties appProperties;
}
Properties object is configured through config-file. applicationContext.xml
<util:properties id="appProperties" location="classpath:application.properties"/>
I want to test some methods of this implementation.
Question:How to access MySystemServiceImpl-object from test class in such way that Properties appProperties will be initialized properly?
public class MySystemServiceImplTest {
//HOW TO INITIALIZE PROPERLY THROUGH SPRING?
MySystemServiceImpl testSubject;
#Test
public void methodToTest(){
Assert.assertNotNull(testSubject.methodToTest());
}
}
I can't simple create new MySystemServiceImpl - than methods that use appProperties throws NullPointerException. And I can't directly inject properties in the object - there is no appropriate setter-method.
Just put correct steps here (thanks to #NimChimpsky for answer):
I copied application.properties under test/resources dir.
I copied applicationContext.xml under test/resources dir. In application context I add new bean (definition of application properties is already here):
<bean id="testSubject" class="com.package.MySystemServiceImpl">
I modified test class in such way:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext.xml"})
public class MySystemServiceImplTest {
#Autowired
MySystemServiceImpl testSubject;
}
And this make the trick - now in my test class fully functional object is available
Alternatively, to do an integration test I do this.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-test.xml"})
#Transactional
public class MyTest {
#Resource(name="myService")
public IMyService myService;
Then use the service as you would normally. Add the app context to your test/resources directory
Just use its constructor :
MySystemServiceImpl testSubject = new MySystemServiceImpl();
This is a unit test. A unit test tests a class in isolation from the other classes and from the infrastructure.
If your class has dependencies over other interfaces, mock those interfaces and create the object with these mocks as argument. That's the whole point of dependency injection : being able to inject other, mock implementations inside an object in order to test this object easily.
EDIT:
You should provide a setter for your properties object, in order to be able to inject the properties you want for each of the unit tests. The injected properties could contain nominal values, extreme values, or incorrect values depending on what you want to test. Field injection is practical, but doesn't fit well with unit-testing. Constructor or setter injection should be preferred when unit-testing is used, because the main goal of dependency injection is precisely to be able to inject mock or specific dependencies in unit tests.

Categories