Injecting @Autowired private field during testing

Injecting @Autowired private field during testing



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.


@Service



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?


@Before



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 applicationContent just for my tests.


@Autowired



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)


org.mockito.Mockito


Mockito.mock(MyClass.class)



Many thanks.




6 Answers
6



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()


}





what is the difference between using @RunWith.... and dont use it?
– jpganz18
Jan 6 '16 at 21:36





@jpganz18 with junit you normally use that, it's like calling MockitoAnnotations.initMocks before each method
– fd8s0
Mar 4 '16 at 9:18





Right now, I'm using this runner: @RunWith(SpringRunner.class) Can I substitute the MockitoJUnitRunner? If not, is there any way to inject the Mocks without it? (I'm not really using mock objects. I want to inject the same Beans the application uses.)
– MiguelMunoz
Feb 14 at 23:20



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.


MockitoJUnitRunner


@InjectMocks



If you use Spring, you already have a utility class for this : org.springframework.test.util.ReflectionTestUtils


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);






Great answer! I had to include mvnrepository.com/artifact/org.springframework/spring-test in my pom.xml to get ReflectionTestUtils class.
– user674669
Mar 16 at 20:43





I would say it's a terrible advice. There are at least two reasons why: 1. anything requires reflection usually smells bad and points to architecture problems 2. the whole point of dependency injection is to be able to replace injected object easily, so again, using reflection you missed that point
– artkoshelev
May 7 at 8:02






@artkoshelev I agree that for testing purpose and "architectural" concerns, construcotr injecton is cleaner and is to be recommended. It also plays better with java config. But if you want to test some existing beans that use field injection and can't or won't modify them, I would argue that it is better to write a test that uses reflection in the setup than no test at all... Besides if reflection is always a code smell or archiecture problem, than Spring is a code smell, Hibernate is a code smell, and so on...
– Pierre Henry
May 8 at 23:55





"Besides if reflection is always a code smell or archiecture problem, than Spring is a code smell, Hibernate is a code smell, and so on..." i certainly wasn't talking about well-tested and well-known frameworks, but actual application code developers wrote.
– artkoshelev
May 9 at 6:51



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


@Autowired


@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.


src/main/java


src/main/test



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 @InitMocks and in a blog post that I have written.


setUp()



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






This is the correct way to load the Spring Context!! but i think better you create a test context for your tests..
– Tiarê Balbi
May 8 '13 at 2:53





This isn't necessarily the correct way, it all depends on what you're trying to accomplish. What if you're setting up hibernate sessions in your app context? Do you really want to hit a real database with a unit test? Some people might say "yes", not realizing that they're creating an integration test and possibly mucking up their data. On the other hand, if you created a test app context and point hibernate to an embedded database then your data is better off, but you're still creating an integration test, not a unit test.
– Dan
Feb 13 '15 at 0:15





This is fo so-called "integration tests" when you want to test your components together with the whole context. It is of course useful, but not the same as unit test where you want to test a single class in isolation.
– Pierre Henry
Sep 16 '16 at 8:02



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 :)



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:






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)