Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

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

like image 785
Kyle Avatar asked May 07 '13 18:05

Kyle


People also ask

Can we Autowired private fields?

The Spring Framework does allow you to autowire private fields. You do see people doing this. And Spring will perform some reflection magic to perform dependency injection.

Should Autowired fields be private?

I would generally NOT use @Autowired for private fields or methods. @Autowired means, somebody from outside will set this field. "Private" on the other hand means nobody except this class is allowed to use it.

Can we use @autowired in Test class?

To check the Service class, we need to have an instance of the Service class created and available as a @Bean so that we can @Autowire it in our test class. We can achieve this configuration using the @TestConfiguration annotation.

Can we use @autowired in jUnit?

Also note that we can wire other spring beans in our jUnit test classes using @Autowired annotation.


2 Answers

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() {      } } 
like image 166
Manuel Quinones Avatar answered Oct 13 '22 06:10

Manuel Quinones


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);         }     } 
like image 22
Pierre Henry Avatar answered Oct 13 '22 05:10

Pierre Henry