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

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.

Related

CDI instantiate and inject parent-child type beans is either ambigious or child types is not found

I have an interface like:
public interface DateTimeService {
ZonedDateTime now();
void fixTo(ZonedDateTime date);
void forget();
}
and I have two implementations of it. One for production where fixTo and forget throws exceptions and one for testing where we control time. Then I have a CDI config that depending on a flag instantiate the right type.
#ApplicationScoped
public class Configuration {
#Produces
#ApplicationScoped
public DateTimeService dateTimeService(Configuration config) {
if (config.isFakeDateTimeServiceEnabled()) {
return new FakeDateTimeService();
} else {
return new DefaultDateTimeService();
}
}
}
However, I wanted to remove fixTo and forget from DateTimeService as they are only there so we can control time in tests. I made a new interface like:
public interface FakeDateTimeService extends DateTimeService {
// fixto and forget is moved from DateTimeService to here
}
I have a few injection points. Some is in production code, some is in test code. In production code I would like to only be able to access DateTimeSerice, in test code I want be able to get a handle on the extended service.
In prod code:
#Inject
private DateTimeService dateTimeService;
In test code:
#Inject
private FakeDateTimeService dateTimeService;
If I leave the config unchanged, then the test code will never find my extended service (as CDI seems to ignore the run-time type of the instance produced by the producer method).
If I update the config to instantiate both (in this case I can even inject the genuine service into the fake one), then production cannot wire together as fake also implements the DateTimeService interface and it causes ambiguity. At this point I could probably just use a qualifier, but I neither want to change my production code because of this nor have to have a fake date time service exist in production context.
I tried to veto bean creation but I failed to do so.
What I thought would/should work is instantiate the right type and programmatically add it to the bean context but examples I found was for CDI 2.0 while for now, relevant part of my code is stuck on CDI 1.2.
Probably at this point you can tell, that I'm not a CDI expert. So I'm opened for suggestion on good CDI read materials as well as concrete suggestions on this problem.
Otherwise, I'm about to give up and just live with a DateTimeService that have fixTo and forget methods.
Depending on how you are doing it, you could use the following approaches, whereby some of them have already be mentioned in the comments.
With alternatives
For testing you provide an own beans.xml, which defines the alternative for testing, which replaces the production one. You will have to keep them in sync, whereby the test beans.xml contains changes necessary for testing. Depending on the CDI implementation the beans.xml can be left out and #Alternative will be enough.
https://docs.oracle.com/javaee/6/tutorial/doc/gjsdf.html
interface DateTimeService { ... }
// Active during production, beans.xml does not define alternative
class DefaultDateTimeService implements DateTimeService { ... }
// Active during test, beans.xml defines alternative
#Alternative
class FakeDateTimeService implements DateTimeService { ... }
// Injection point stays the same.
#Inject
DateTimeService dateTimeService;
// Used in test environment
<beans ... >
<alternatives>
<class>FakeDateTimeService</class>
</alternatives>
</beans>
With Arquillian
With Aarquillian you can assemble the deployment yourself and exclude the DefaultDateTimeService implementation and replace it with the FakeDateTimeService implementation. With this approach you don't need to hack anything because during test, only FakeDateTimeService implementation will be visible to the CDI container.
http://arquillian.org/guides/getting_started/
With CDI Extension
For testing you could provide an CDI extension and the FakeDateTimeService implementation in the test code, whereby the CDI Extension does veto the DefaultDateTimeService implementation.
package test.extension
public class VetoExtension implements Extension {
public <T> void disableBeans(#Observes ProcessAnnotatedType<T> pat) {
// type matching
if (DefaultDateTimeService.class.isAssignableFrom(pat.getAnnotatedType().getJavaClass())) {
pat.veto();
}
}
}
// Define SPI provider file, assuming test/resources is on CDI classpath
src/test/resources/META-INF/services/javax.enterprise.inject.spi.Extension
// Single line you put in there
test.extension.VetoExtension
Deltaspike #Exclude
Deltaspike is a CDI extension library which provides some useful utilities for CDI development and testing, also a CDITestRunner is provided. The #Exclude annotation is used by an extension which does the same as the example above but already implemented for you.
https://deltaspike.apache.org/
https://deltaspike.apache.org/documentation/test-control.html
https://deltaspike.apache.org/documentation/projectstage.html
You should choose an approach which doesn't pollute your source code and forces you to hack to much to get it running for testing.
I would prefer Arquillian because here you have full control about what is in the deployment and nothing in your production code has to change or be specially implemented so its testable.
With Deltaspike you can exclude production code for testing and replace them with test implementations.
If no additional library or framework can be used I would prefer the alternative approach, because its the least complicated one to use.

Spring MVC 4 for REST Controller, how to autowire different dependencies for test cases

Let's say you have a controller like so and you want to change the autowired dependency. What are ways to have one implementation for your project under /src/main/java and then have a mock dependency in your test code under /src/test/java. The code under /src/main/java will get built into the web application.
#RestController
#RequestMapping("/u/util/")
public class HealthCheckAdapter {
#Autowired
private HealthDbService healthCheck;
}
The HealthDbService is a Java interface, and here an example concrete class. I could change the qualifier below, but Spring will still pick this up in the test cases?
#Repository
#Qualifier("basicHealthCheckService")
public class HealthCheckDefaultManager implements HealthDbService {
}
Simplest, recommended way: use constructor injection:
#RestController
#RequestMapping("/u/util/")
public class HealthCheckAdapter {
private HealthDbService healthCheck;
#Autowired // not even needed in recent versions
public HealthCheckAdapter(HealthDbService healthCheck) {
this.healthCheck = healthCheck;
}
}
And now in your unit test:
new HealthCheckAdapter(new FakeHealthDbService());
or, using a mocking API like Mockito:
HealthDbService mockHealthDbService = mock(HealthDbService.class);
new HealthCheckAdapter(mockHealthDbService);
If you use Mockito, it can also inject mock dependencies using annotations, even if you keep using field injection.
And finally, if you want to use mock MVC tests (to test the annotations, the JSON serialization, etc.), you can use the Spring testing support (which is even easier to use in Spring Boot)

Autowired implementation based on profile Spring Boot

I am developing a REST API with Spring Boot.The problem it's that I have one interface and two implementations and I want to test only with the mock implementation.
Interface CRMService
#Service
CRMServiceImpl
#Service
CRMServiceMock
Implementations: the first one is the real integration with the backend and the second is a mock for testing purposes, what's the best approach? Integration test or test based on the active profile ? If I need to autowire a service based on profile what's the best practice?
While I'm sure there's exceptions, generally it shouldn't be integration or unit tests (often involves mocks), but both; see testing pyramid concept.
Integration tests: just use the real service. If it calls out to other live services, then consider injecting the URLs as Spring Boot properties which point to mock servers in the test environment (Node.js or something easy and quick).
Unit tests: Consider using a test-framework like Mockito. Using this you can write your tests with mocks approximately like so:
private CRMServiceImpl mockService = mock(CRMServiceImpl.class);
#Test
public void someTest() {
when(mockService.someMethod(any(String.class), eq(5))).thenReturn("Hello from mock object.")
}
The above example roughly translates to "when some class invokes 'someMethod(String, int)' on your service, return the String specified".
This way allows you to still use mocks where necessary, but avoids having to maintain entire mock implementation profiles and avoids the problem of what to auto-wire.
Finally, if you need a full separate implementation, consider not auto-wiring services! Instead, use #Bean annotations in your configuration class and inject it via constructors into the classes that need it. Something like so:
#Configuration
public class ApplicationConfiguration {
#Value{$"service.crm.inmem"} // Injected property
private boolean inMem;
#Bean
CRMService getCRMService() {
if (inMem) {
return new CRMServiceMock();
}
return new CRMServiceImpl();
}
#Bean
OtherService getOtherService() {
// Inject CRMService interface into constructor instead of auto-wiring in OtherService.class
return new OtherService(getCRMService());
}
}
An example of when you could use ^^ would be if you wanted to switch between an in-memory store, and a real database-connection layer.
Personally I'd suggest doing dependency injection like the above example even when there aren't multiple implementations since as a project grows, if an auto-wired property fails it can be difficult to track down exactly why. Additionally explicitly showing where dependencies come from can help with organizing your application and visualizing your application hierarchy.

Using a Class with a #Required Field in Unit Test with Mockito

I'm trying to unit test a class; for the sake of brevity we'll call it Apple. It has a #Required setter that takes an AppleManager bean. AppleManager itself has a #Required setter than takes an AppleStorageClient bean. I am mocking AppleManager and injecting it into Apple, but I also need to use the real AppleManager class to retrieve data using methods from its AppleStorageClient object. How can I achieve this with Spring/Mockito?
Test:
public class AppleTest {
#InjectMocks
private Apple apple;
#Mock
private AppleManager appleManager;
?????
private AppleManager realAppleManager;
//I tried = new AppleManager() here but the object is null...
//ostensibly because Spring doesn't know I want to use the bean
//also tried #Autowired to no avail
#Before
public void doBeforeStuff() {
MockitoAnnotations.initMocks(this);
}
...
}
Source:
public class Apple {
private AppleManager appleManager;
#Required
public void setAppleManager(AppleManager appleManager) {
this.appleManager = appleManager;
}
....
}
&
public class AppleManager {
private AppleStorageClient appleStorageClient;
#Required
public void setAppleStorageClient() {
this.appleStorageClient = appleStorageClient;
}
...
}
In general it looks like something is 'uncomplete' here. I'll explain why.
Technically If you're using spring - it doesn't sound like a unit test to me anymore, probably integration test or something.
Unit tests are in general should be really-really fast and starting up spring won't let them pass fast enough (think about having thousands of unit tests in your project each of them running spring on startup - it will take them ages to complete).
But let's say its only about definitions. When you're using spring testing framework with JUnit, someone has to start and maintain a spring context to do all the Dependency Injection magic and apply it to the test case.
In Junit implementation a special Runner (a JUnit abstraction) is required:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:my-test-context.xml" }) // or use Java Config
This doesn't appear in the question though.
So now Spring will create a context and will attempt to inject beans. And we have effectively reduced our issue to the issue of having two implementations of the interface and asking spring to inject implementation by interface so that two different implementations will be injected. There are 2 solutions I can see here:
Create a Mock outside spring - you probably won't specify your expectations in spring anyway. Maintain only a "real apple manager" in spring
Maintain both in spring but in your test case use a #Qualifier annotation
Now what I would like to emphasize is that if you maintain real apple manager that contacts "apple store" (probably a database, with driver support, transaction management and so forth) you'll have to create a test context so that it will be able to connect to that database, and if the apple manager internally injects its dependencies via spring, then these beans are also have to be specified.
So that if in future you'll change something in the underlying store (say, add a dependency in a driver to another spring bean, this test context will automatically become broken). Just be aware of this and inject beans wisely.

How to mock annotated EJBs?

I googled it already, but seems kinda hard to find topics on mocking dependency injected objects (EJB 3.0).
public class MyTestBean
{
#EJB
ILoginService mLoginService;
public void doLogin() {
if (!mLoginService.login(name, pass)) {
// fehler
}
}
When running tests with openEJB I want to have that LoginService.login(name, pass) return true. Is there a way to mock the LoginService bean?
(Currently the login-methode uses some JAAS-stuff i can't emulate during tests.)
May be have a look at Mockito. You can the apply #EJB annotation on a setter and inject mocked LoginService in your tests.
Another option is to simply provide a second implementation of your ILoginService interface. This second implementation is the mock, but no special Mock library or support is necessary for this.
You put this implementation is a special source folder, typically called test. Then you build your deployment scripts so that for normal builds this test folder is ignored. When you do your unit tests and build the archive for testing, you explicitly include the Mock implementation from the test source folder.

Categories