Hi i have implemented a series of classes that use dagger 2 Injection to inject it dependencies for each class and i am trying to mock these to run my unit tests but it fails to initialise the dependencies found from the lower tier of classes that have dependencies.
This works fine in real enviornment but not for testing
I tried marking all the dependencies as either Spy or Mock but it only seems to inject the mocks on the first layer of my class that gets invoked. Below are three classes marked ExampleOne,Two,Three, followed by the test class.
ExampleOne has a method that calls another method from ExampleTwo, that then finally calls another from ExampleThree.
Only ExampleTwo gets the mock/spy injected fine but not ExampleThree.
public class ExampleOne{
#Inject
ExampleTwo exampleTwo;
//implementations below
public void doSomethingOne(){
exampleTwo.doSomethingTwo;
}
}
public class ExampleTwo{
#Inject
ExampleThree exampleThree
public void doSomethingTwo(){
exampleThree.doPrintHello();
}
}
public class ExampleThree{
public void doPrintHello(){
Log.d("Print","Hello")
}
}
Below is my test
#RunWith(MockitoJUnitRunner.class)
public class TestExamples(){
#InjectMocks
ExampleOne exampleOne
#Spy
ExampleTwo exampleTwo = new ExampleTwo();
#Spy
ExampleThree exampleThree = new ExampleThree();
#Test
void test(){
exampleOne.doSomethingOne();
//some testing code here
}
}
Practice explicit dependency principle either via constructor injection or method injection. Next, unit tests should be isolated. You should have no need to access implementation concerns in this case. Your classes are tightly coupled to implementation concerns and not abstractions which is a code smell.
public class ExampleOne {
ExampleTwo exampleTwo;
#Inject
public ExampleOne(ExampleTwo exampleTwo) {
this.exampleTwo = exampleTwo;
}
//implementations below
public void doSomethingOne(){
exampleTwo.doSomethingTwo();
}
}
public interface ExampleTwo {
void doSomethingTwo();
}
public class ConcreteExampleTwo implements ExampleTwo {
private ExampleThree exampleThree;
#Inject
public ConcreteExampleTwo(ExampleThree exampleThree) {
this.exampleThree = exampleThree;
}
public void doSomethingTwo(){
exampleThree.doPrintHello();
}
}
public interface ExampleThree {
void doPrintHello();
}
//...code removed for brevity
ExampleOne has one dependency at that level and if that dependencies are not able to be mocked/stubbed/faked without side effects then there is a problem with the design of the target class.
#RunWith(MockitoJUnitRunner.class)
public class TestExamples(){
#Mock
ExampleTwo exampleTwo;
#InjectMocks
ExampleOne exampleOne
#Test
void test(){
exampleOne.doSomethingOne();
verify(exampleTwo).doSomethingTwo();
}
}
With the above suggested changes ExampleOne can be tested in isolation without any knock on effects.
The concrete implementation of ExampleTwo can also be tested in isolation as well.
Related
I'm struggling with writing unit tests for one of my classes. The main class:
public class MainClass {
...
private Instance<BaseInterface> steps;
#Inject
public void setSteps(Instance<BaseInterface> steps) { this.steps = steps; }
#Asynchronous
public void callingMethod() {
...
ImmutableList<BaseInterface> stepList = STEP_ORDERING.immutableSortedCopy(steps); // Here the NPE is being thrown when calling method from test class
...
}
}
There are 5 specific implementations of BaseInterface, all 5 are correctly injected through setter by CDI at runtime (one of the implementation is annotated with #Named, the rest do not have this annotation).
However, calling the wrapper method callingMethod from the test class throws NPE. The test class:
#RunWith(MockitoJUnitRunner.class)
public class MainClassTest {
#InjectMocks
private MainClass mainClass = new MainClass();
#Mock
private Instance<BaseInterface> steps;
#Test
public void test() {
...
mainClass.callingMethod(); // Throws NPE
...
}
}
As far as I'm concerned, Mockito does not take care of bean dependency injection like CDI. Therefore, is there a way in which to tell Mockito to mock all implementations of BaseInterface and inject them into the MainClass?
Version details: Java 8, Mockito 2.8.9, JUnit 4
Thank you!
Solved. The problem was not caused by Mockito, but by the method STEP_ORDERING.immutableSortedCopy which behind the scenes calls iterator() method from Instance<T> which returned null (since it extends Iterable<T>).
Mocking the iterator does the trick:
Iterator<BaseInterface> iteratorMock = (Iterator<BaseInterface>) mock(Iterator.class);
when(steps.iterator()).thenReturn(iteratorMock);
or by creating an actual instance of iterator:
Iterator<BaseInterface> iteratorActual = new Iterator<BaseInterface>(){
// implementation here
};
when(steps.iterator()).thenReturn(iteratorActual );
I have a Spring component, which takes a Clock as a dependency:
#Component
public class MyClass {
private final Clock clock;
#Autowired
public MyClass(Clock clock){
this.clock = clock
}
}
For most of my tests, I have a mock Clock that is annotated as a #Bean and is correctly injected. However, for some of my tests, I need to use a different mocked clock. How can I use a different mock for these tests?
public class MyClassTest {
#Autowired
private MyClass myClass;
#Test
public void test1(){
// use the standard mock clock
}
#Test
public void test2(){
// inject clock.fixed(Instant.parse("2018-01-01T00:00:00Z"), UTC);
}
#Test
public void test3(){
// inject clock.fixed(Instant.parse("2019-12-12T23:59:59Z"), UTC);
}
}
So in this example, I want to override the mocked clock in test2 and test3.
You can use ReflectionTestUtils to inject the various mocks. It was made for this purpose.
#Test
public void test2(){
// inject clock.fixed(Instant.parse("2018-01-01T00:00:00Z"), UTC);
ReflectionTestUtils.setField(myClass, "clock", mockClock);
}
I think the proper way to deal with such usecase is using Mockito lib with only one bean and just change the stub method implementation on demand:
#Configuration
public class TestConfig {
#Bean
public Clock clock(){
return Mockito.mock(Clock.class);
}
}
public class MyClassTest {
#Autowired
private Clock clock;
#Autowired
private MyClass myClass;
#Test
public void test1(){
// use the standard mock clock
}
#Test
public void test2(){
Mockito.when(clock.giveMeAValue()).thenReturn(Instant.parse("2018-01-01T00:00:00Z"), UTC);
}
#Test
public void test3(){
Mockito.when(clock.giveMeAValue()).thenReturn(Instant.parse("2019-12-12T23:59:59Z"), UTC);
}
}
Mockito is already provided by spring-boot-starter-test.
Let's look to all options that you have.
Replace dependency manually
Like it was advised in other answers you can use ReflectionTestUtils if the field is private or create a setter. This is a simple method but it has some downsides you should be aware of.
If MyClass invokes some other class OtherClockClient that as well uses clock that class is unaware of such replacement and you may get inconsistency because two clocks a hanging around.
The other problem is that manual replacement corrupts spring context. It is important because spring context is reused between tests by default and test order is not guaranteed. It means that it may happen that test2 will be executed before test1 and "standard" clock will not be used in test1. If test1 relies on the behaviour of implicitly set "standard" clock you are in trouble.
To solve this you either can mark test with #DirtiesContext or manually revert the dependency.
Single Dispatcher
You can always inject the same clock in tests and by default it should demonstrate the behaviour you named "standard". However it can provide additional API that allows tests to configure the clock for their need.
public class ConfigurableClock implements Clock {
void fixAt(Instant instant) {
}
}
public class MyClassTest {
#Autowired
private ConfigurableClock clock;
#Test
public void test1() {
// use standard clock behaviour
}
#Test
public void test2(){
clock.fixAt(Instant.parse("2018-01-01T00:00:00Z"));
}
In this case there is no need to rewire beans but the state should still be reset so that tests that expect implicit default clock have it. You can either do reset of the clock in #Before/#After methods:
#Before
public setUp() {
clock.reset();
}
#After
public tearDown() {
clock.reset();
}
Or add a TestExecutionListener that will do that.
Let's say I have a class (OrchestratingClass) that has a method orchestrate() that calls a number of smaller methods of other classes (classA's do1() method, classB's do2() method). I would like to test the behavior of orchestrate() by mocking the responses of do1() and do2() with various permutations. I'm running my test with something like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public OrchestratingClassTest {
#Inject
private OrchestratingClass oc;
#Test
public void test1() {
// I would like to mock classA's do1() method to send back "1"
// I would like to mock classB's do2() method to send back "2"
}
#Test
public void test2() {
// I would like to mock classA's do1() method to send back "100"
// I would like to mock classB's do2() method to send back "200"
}
static class SimpleConfig {
#Bean
public InterfaceA interfaceA() {
return new ClassA();
}
#Bean
public InterfaceB interfaceB() {
return new ClassB();
}
#Bean
public OrchestratingClass orchestratingClass() {
return new OrchestratingClass();
}
}
}
And the orchestratingClass itself is quite basic, but I've added some sample code to aid in visualization:
#Named
public OrchestratingClass {
#Inject
private InterfaceA interfaceA;
#Inject
private InterfaceB interfaceB;
public String orchestrate() {
return interfaceA.do1() + " " + interfaceB.do2();
}
}
Now I'm aware I could tweak my SimpleConfig class to have the mocked out versions of classA and classB, but then I'm locked into 1 particular mock and can't "re-mock" things when I move onto test2(). I'm convinced that playing around with the java config files for 1 single test class would not work if we're trying to inject different "flavors" of beans "per-test". Does anyone have any recommendation on what I can do to make sure I'm really testing this thoroughly without being invasive (ex: adding superfluous "setters" for the specific classes in the orchestratingClass to side-step the bean injection pain)? Essentially, I'm looking to "tamper" the applicationContext on a per-test basis for specific beans of interest (along with the necessary housekeeping that's required) by applying a variety of mocks.
Here's a simple example using Mockito:
public class OrchestratingClassTest {
#Mock
private ClassA mockA;
#Mock
private ClassB mockB;
#InjectMocks
private OrchestratingClass oc;
#Before
public void prepare() {
MockitoAnnotations.initMocks(this);
}
#Test
public void shouldConcatenate() {
when(mockA.do1()).thenReturn("1");
when(mockB.do2()).thenReturn("2");
assertEquals("1 2", oc.orchestrate());
}
}
The magic happens in the call to MockitoAnnotations.initMocks(this), which will create mock instances for the fields annotated with #Mock, then create a real instance of Orchestrating class and inject its fields, thanks to #InjectMocks.
Note that, even without this magic, you could make your class easily testable by just adding a constructor taking ClassA and ClassB as arguments to OrchestratingClass, and annotate that constructor with #Autowired instead of annotating the fields. So, in short, use constructor injection rather than field injection.
I have an Android app that uses Dagger 2 for dependency injection. I am also using the latest gradle build tools that allow a build variant for unit testing and one for instrumentation tests. I am using java.util.Random in my app, and I want to mock this for testing. The classes I'm testing don't use any Android stuff, so they're just regular java classes.
In my main code I define a Component in a class that extends the Application class, but in the unit tests I'm not using an Application. I tried defining a test Module and Component, but Dagger won't generate the Component. I have also tried using the Component that I defined in my application and swapping the Module when I build it, but the application's Component doesn't have inject methods for my test classes. How can I provide a mock implementation of Random for testing?
Here's some sample code:
Application:
public class PipeGameApplication extends Application {
private PipeGame pipeGame;
#Singleton
#Component(modules = PipeGameModule.class)
public interface PipeGame {
void inject(BoardFragment boardFragment);
void inject(ConveyorFragment conveyorFragment);
}
#Override
public void onCreate() {
super.onCreate();
pipeGame = DaggerPipeGameApplication_PipeGame.create();
}
public PipeGame component() {
return pipeGame;
}
}
Module:
#Module
public class PipeGameModule {
#Provides
#Singleton
Random provideRandom() {
return new Random();
}
}
Base class for tests:
public class BaseModelTest {
PipeGameTest pipeGameTest;
#Singleton
#Component(modules = PipeGameTestModule.class)
public interface PipeGameTest {
void inject(BoardModelTest boardModelTest);
void inject(ConveyorModelTest conveyorModelTest);
}
#Before
public void setUp() {
pipeGameTest = DaggerBaseModelTest_PipeGameTest.create(); // Doesn't work
}
public PipeGameTest component() {
return pipeGameTest;
}
}
or:
public class BaseModelTest {
PipeGameApplication.PipeGame pipeGameTest;
// This works if I make the test module extend
// the prod module, but it can't inject my test classes
#Before
public void setUp() {
pipeGameTest = DaggerPipeGameApplication_PipeGame.builder().pipeGameModule(new PipeGameModuleTest()).build();
}
public PipeGameApplication.PipeGame component() {
return pipeGameTest;
}
}
Test Module:
#Module
public class PipeGameTestModule {
#Provides
#Singleton
Random provideRandom() {
return mock(Random.class);
}
}
This is currently impossible with Dagger 2 (as of v2.0.0) without some workarounds. You can read about it here.
More about possible workarounds:
How do you override a module/dependency in a unit test with Dagger 2.0?
Creating test dependencies when using Dagger2
You have hit the nail on the head by saying:
application's Component doesn't have inject methods for my test classes
So, to get around this problem we can make a test version of your Application class. Then we can have a test version of your module. And to make it all run in a test, we can use Robolectric.
1) Create the test version of your Application class
public class TestPipeGameApp extends PipeGameApp {
private PipeGameModule pipeGameModule;
#Override protected PipeGameModule getPipeGameModule() {
if (pipeGameModule == null) {
return super.pipeGameModule();
}
return pipeGameModule;
}
public void setPipeGameModule(PipeGameModule pipeGameModule) {
this.pipeGameModule = pipeGameModule;
initComponent();
}}
2) Your original Application class needs to have initComponent() and pipeGameModule() methods
public class PipeGameApp extends Application {
protected void initComponent() {
DaggerPipeGameComponent.builder()
.pipeGameModule(getPipeGameModule())
.build();
}
protected PipeGameModule pipeGameModule() {
return new PipeGameModule(this);
}}
3) Your PipeGameTestModule should extend the production module with a constructor:
public class PipeGameTestModule extends PipeGameModule {
public PipeGameTestModule(Application app) {
super(app);
}}
4) Now, in your junit test's setup() method, set this test module on your test app:
#Before
public void setup() {
TestPipeGameApp app = (TestPipeGameApp) RuntimeEnvironment.application;
PipeGameTestModule module = new PipeGameTestModule(app);
app.setPipeGameModule(module);
}
Now you can customize your test module how you originally wanted.
In my opinion you can approach this problem by looking at it from a different angle. You will easily be able to unit test your class by not depending upon Dagger for construction class under test with its mocked dependencies injected into it.
What I mean to say is that in the test setup you can:
Mock the dependencies of the class under test
Construct the class under test manually using the mocked dependencies
We don't need to test whether dependencies are getting injected correctly as Dagger verifies the correctness of the dependency graph during compilation. So any such errors will be reported by failure of compilation. And that is why manual creation of class under test in the setup method should be acceptable.
Code example where dependency is injected using constructor in the class under test:
public class BoardModelTest {
private BoardModel boardModel;
private Random random;
#Before
public void setUp() {
random = mock(Random.class);
boardModel = new BoardModel(random);
}
#Test
...
}
public class BoardModel {
private Random random;
#Inject
public BoardModel(Random random) {
this.random = random;
}
...
}
Code example where dependency is injected using field in the class under test (in case BoardModel is constructed by a framework):
public class BoardModelTest {
private BoardModel boardModel;
private Random random;
#Before
public void setUp() {
random = mock(Random.class);
boardModel = new BoardModel();
boardModel.random = random;
}
#Test
...
}
public class BoardModel {
#Inject
Random random;
public BoardModel() {}
...
}
If you are using dagger2 with Android, you can use app flavours for providing mocking resources.
See here for a demo of flavours in mock testing(without dagger):
https://www.youtube.com/watch?v=vdasFFfXKOY
This codebase has an example:
https://github.com/googlecodelabs/android-testing
In your /src/prod/com/yourcompany/Component.java
you provide your production components.
In your /src/mock/com/yourcompany/Component.java
you provide your mocking components.
This allows you create builds of your app with or without mocking.
It also allows parallel development (backend by one team, frontend app by another team), you can mock until api methods are avilable.
How my gradle commands look (its a Makefile):
install_mock:
./gradlew installMockDebug
install:
./gradlew installProdDebug
test_unit:
./gradlew testMockDebugUnitTest
test_integration_mock:
./gradlew connectedMockDebugAndroidTest
test_integration_prod:
./gradlew connectedProdDebugAndroidTest
I actually had the same issue and found a very simple solution.
This is not the best possible solution I think but it will solve your problem.
Create a similar class in your app module:
public class ActivityTest<T extends ViewModelBase> {
#Inject
public T vm;
}
Then, in your AppComponent add:
void inject(ActivityTest<LoginFragmentVM> activityTest);
Then you will be able to inject that in your test class.
public class HelloWorldEspressoTest extends ActivityTest<LoginFragmentVM> {
#Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class);
#Test
public void listGoesOverTheFold() throws InterruptedException {
App.getComponent().inject(this);
vm.email.set("1234");
closeSoftKeyboard();
}
}
I have a component setup that is essentially a launcher for an application. It is configured like so:
#Component
public class MyLauncher {
#Autowired
MyService myService;
//other methods
}
MyService is annotated with the #Service Spring annotation and is autowired into my launcher class without any issues.
I would like to write some jUnit test cases for MyLauncher, to do so I started a class like this:
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
#Test
public void someTest() {
}
}
Can I create a Mock object for MyService and inject it into myLauncher in my test class? I currently don't have a getter or setter in myLauncher as Spring is handling the autowiring. If possible, I'd like to not have to add getters and setters. Can I tell the test case to inject a mock object into the autowired variable using an #Before init method?
If I'm going about this completely wrong, feel free to say that. I'm still new to this. My main goal is to just have some Java code or annotation that puts a mock object in that #Autowired variable without me having to write a setter method or having to use an applicationContext-test.xml file. I would much rather maintain everything for the test cases in the .java file instead of having to maintain a separate application content just for my tests.
I am hoping to use Mockito for the mock objects. In the past I have done this by using org.mockito.Mockito and creating my objects with Mockito.mock(MyClass.class).
You can absolutely inject mocks on MyLauncher in your test. I am sure if you show what mocking framework you are using someone would be quick to provide an answer. With mockito I would look into using #RunWith(MockitoJUnitRunner.class) and using annotations for myLauncher. It would look something like what is below.
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher = new MyLauncher();
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
The accepted answer (use MockitoJUnitRunner and #InjectMocks) is great. But if you want something a little more lightweight (no special JUnit runner), and less "magical" (more transparent) especially for occasional use, you could just set the private fields directly using introspection.
If you use Spring, you already have a utility class for this : org.springframework.test.util.ReflectionTestUtils
The use is quite straightforward :
ReflectionTestUtils.setField(myLauncher, "myService", myService);
The first argument is your target bean, the second is the name of the (usually private) field, and the last is the value to inject.
If you don't use Spring, it is quite trivial to implement such a utility method. Here is the code I used before I found this Spring class :
public static void setPrivateField(Object target, String fieldName, Object value){
try{
Field privateField = target.getClass().getDeclaredField(fieldName);
privateField.setAccessible(true);
privateField.set(target, value);
}catch(Exception e){
throw new RuntimeException(e);
}
}
Sometimes you can refactor your #Component to use constructor or setter based injection to setup your testcase (you can and still rely on #Autowired). Now, you can create your test entirely without a mocking framework by implementing test stubs instead (e.g. Martin Fowler's MailServiceStub):
#Component
public class MyLauncher {
private MyService myService;
#Autowired
MyLauncher(MyService myService) {
this.myService = myService;
}
// other methods
}
public class MyServiceStub implements MyService {
// ...
}
public class MyLauncherTest
private MyLauncher myLauncher;
private MyServiceStub myServiceStub;
#Before
public void setUp() {
myServiceStub = new MyServiceStub();
myLauncher = new MyLauncher(myServiceStub);
}
#Test
public void someTest() {
}
}
This technique especially useful if the test and the class under test is located in the same package because then you can use the default, package-private access modifier to prevent other classes from accessing it. Note that you can still have your production code in src/main/java but your tests in src/main/test directories.
If you like Mockito then you will appreciate the MockitoJUnitRunner. It allows you to do "magic" things like #Manuel showed you:
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher; // no need to call the constructor
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
Alternatively, you can use the default JUnit runner and call the MockitoAnnotations.initMocks() in a setUp() method to let Mockito initialize the annotated values. You can find more information in the javadoc of #InjectMocks and in a blog post that I have written.
I believe in order to have auto-wiring work on your MyLauncher class (for myService), you will need to let Spring initialize it instead of calling the constructor, by auto-wiring myLauncher. Once that is being auto-wired (and myService is also getting auto-wired), Spring (1.4.0 and up) provides a #MockBean annotation you can put in your test. This will replace a matching single beans in context with a mock of that type. You can then further define what mocking you want, in a #Before method.
public class MyLauncherTest
#MockBean
private MyService myService;
#Autowired
private MyLauncher myLauncher;
#Before
private void setupMockBean() {
doNothing().when(myService).someVoidMethod();
doReturn("Some Value").when(myService).someStringMethod();
}
#Test
public void someTest() {
myLauncher.doSomething();
}
}
Your MyLauncher class can then remain unmodified, and your MyService bean will be a mock whose methods return values as you defined:
#Component
public class MyLauncher {
#Autowired
MyService myService;
public void doSomething() {
myService.someVoidMethod();
myService.someMethodThatCallsSomeStringMethod();
}
//other methods
}
A couple advantages of this over other methods mentioned is that:
You don't need to manually inject myService.
You don't need use the Mockito runner or rules.
I'm a new user for Spring. I found a different solution for this. Using reflection and making public necessary fields and assign mock objects.
This is my auth controller and it has some Autowired private properties.
#RestController
public class AuthController {
#Autowired
private UsersDAOInterface usersDao;
#Autowired
private TokensDAOInterface tokensDao;
#RequestMapping(path = "/auth/getToken", method = RequestMethod.POST)
public #ResponseBody Object getToken(#RequestParam String username,
#RequestParam String password) {
User user = usersDao.getLoginUser(username, password);
if (user == null)
return new ErrorResult("Kullanıcıadı veya şifre hatalı");
Token token = new Token();
token.setTokenId("aergaerg");
token.setUserId(1);
token.setInsertDatetime(new Date());
return token;
}
}
And this is my Junit test for AuthController. I'm making public needed private properties and assign mock objects to them and rock :)
public class AuthControllerTest {
#Test
public void getToken() {
try {
UsersDAO mockUsersDao = mock(UsersDAO.class);
TokensDAO mockTokensDao = mock(TokensDAO.class);
User dummyUser = new User();
dummyUser.setId(10);
dummyUser.setUsername("nixarsoft");
dummyUser.setTopId(0);
when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) //
.thenReturn(dummyUser);
AuthController ctrl = new AuthController();
Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao");
usersDaoField.setAccessible(true);
usersDaoField.set(ctrl, mockUsersDao);
Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao");
tokensDaoField.setAccessible(true);
tokensDaoField.set(ctrl, mockTokensDao);
Token t = (Token) ctrl.getToken("test", "aergaeg");
Assert.assertNotNull(t);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
I don't know advantages and disadvantages for this way but this is working. This technic has a little bit more code but these codes can be seperated by different methods etc. There are more good answers for this question but I want to point to different solution. Sorry for my bad english. Have a good java to everybody :)
Look at this link
Then write your test case as
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{
#Resource
private MyLauncher myLauncher ;
#Test
public void someTest() {
//test code
}
}