[java] 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).

This question is related to java spring unit-testing junit autowired

The answer is


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

    }
}

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("Kullaniciadi veya sifre hatali");

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


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

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:

  1. You don't need to manually inject myService.
  2. You don't need use the Mockito runner or rules.

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.


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
   }
}

Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to spring

Are all Spring Framework Java Configuration injection examples buggy? Two Page Login with Spring Security 3.2.x Access blocked by CORS policy: Response to preflight request doesn't pass access control check Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified Spring Data JPA findOne() change to Optional how to use this? After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName The type WebMvcConfigurerAdapter is deprecated No converter found capable of converting from type to type

Examples related to unit-testing

Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0 How to test the type of a thrown exception in Jest Unit Tests not discovered in Visual Studio 2017 Class Not Found: Empty Test Suite in IntelliJ Angular 2 Unit Tests: Cannot find name 'describe' Enzyme - How to access and set <input> value? Mocking HttpClient in unit tests Example of Mockito's argumentCaptor How to write unit testing for Angular / TypeScript for private methods with Jasmine Why is the Visual Studio 2015/2017/2019 Test Runner not discovering my xUnit v2 tests

Examples related to junit

Eclipse No tests found using JUnit 5 caused by NoClassDefFoundError for LauncherFactory How to resolve Unneccessary Stubbing exception JUnit 5: How to assert an exception is thrown? How do I mock a REST template exchange? Class Not Found: Empty Test Suite in IntelliJ Unable to find a @SpringBootConfiguration when doing a JpaTest Failed to load ApplicationContext (with annotation) Example of Mockito's argumentCaptor Mockito - NullpointerException when stubbing Method Spring jUnit Testing properties file

Examples related to autowired

intellij incorrectly saying no beans of type found for autowired repository How do I mock an autowired @Value field in Spring with Mockito? @Autowired - No qualifying bean of type found for dependency Spring can you autowire inside an abstract class? Why is my Spring @Autowired field null? Understanding Spring @Autowired usage @Autowired and static method Injecting @Autowired private field during testing Spring expected at least 1 bean which qualifies as autowire candidate for this dependency Exclude subpackages from Spring autowiring?