Sander_M
Sander_M

Reputation: 1119

Injecting autowired bean using Mockito and setting some properties on the mock

Given the following @Component class:

@Component
public class MovieFinderImpl implements MovieFinder {

    @Autowired
    private Movie movie;

    @Override
    public List<Movie> findAll() {      
        List<Movie> movies = new ArrayList<>();
        movies.add(movie);
        return movies;
    }

}

I am trying to learn how to unit test this example component without doing an integration test (so no @RunWith(SpringRunner.class) and @SpringBootTest annotations on the test class).

When my test class looks like this:

public class MovieFinderImplTest {

    @InjectMocks
    private MovieFinderImpl movieFinderImpl;

    @Mock
    public Movie movieMock;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        movieMock.setTitle("test");
        movieMock.setDirector("directorTest");
    }

    @Test
    public void testFindAll() {         
        List<Movie> movies = movieFinderImpl.findAll();
        Assert.assertNotNull(movies.get(0));

        String expectedTitle = "test";
        String actualTitle = movies.get(0).getTitle();
        Assert.assertTrue(String.format("The expected name is %s, but the actual name is %s", expectedTitle, actualTitle), expectedTitle.equals(actualTitle));

        String expectedDirector = "testDirector";
        String actualDirector = movies.get(0).getDirector();
        Assert.assertTrue(String.format("The expected name is %s, but the actual name is %s", expectedDirector, actualDirector), expectedDirector.equals(actualDirector));
    }
}

... the mock is not null, but the mock class variables are and thus:

java.lang.AssertionError: The expected name is test, but the actual name is null

I have browsed through http://www.vogella.com/tutorials/Mockito/article.html , but was unable to find an example of how to set a class variable on a mock.

How do I properly mock the movie object? More in general is this the proper way to test this MovieFinderImp class? My inspiration of just component testing was this blog https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4

(ps: I wonder if I should actually test movie.get() method in this test class...perhaps my test design is just wrong).

Upvotes: 4

Views: 6834

Answers (3)

borjab
borjab

Reputation: 11675

Mocked objects are not real objects and have no properties only mocked behaviour that you need to add. Using the setter won't do anything to them. The getter will return null unless you specify the spected behaviour. That is can be done using the when... thenReturn structure:

when(movieMock.getTitle()).thenReturn("movie title");

Maybe there is no reason to mock Movie. You can use the @Spy annotation and this way it will be a real object with real properties and and the same time you can override some methods.

Mocks are really usefull when building the full object is hard and have lots of depenencies or complex behaviour but may be an overkill if Movie is a bean. For example:

public class MovieFinderImplTest {

    @InjectMocks
    private MovieFinderImpl movieFinderImpl;

    @Spy /* <- See the change from mock */
    public Movie movie = new Movie();

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        /* modified like a normal object */
        movie.setTitle("test");
        movie.setDirector("directorTest");
    }

See also the difference between mock and spy. There are long discussions in internet about what needs to be mocked and what not. If you still prefer to mock moview then marians27 and VinayVeluri answer is the rigth answer for you.

Upvotes: 5

Vinay Veluri
Vinay Veluri

Reputation: 6865

Here is what you have to do;

import static org.mockito.Mockito.when;

when(movieMock.getTitle()).thenReturn("movie title");
when(movieMock.getDirector()).thenReturn("movie director");

@InjectMocks helps only to inject not null objects and doesnt deal with values. While with values, we have to explicitly set the values we need to test.

If you use, MockitoJunitRunner there is no need to call MockitoAnnotations.initMocks(this); which runner takes care of.

Upvotes: 2

marians27
marians27

Reputation: 311

There is a problem with the way you are mocking in a @Before method. Instead of

movieMock.setTitle("test");
movieMock.setDirector("directorTest");

Do it like that

Mockito.when(movieMock.getTitle()).thenReturn("test");
Mockito.when(movieMock.getDirector()).thenReturn("directorTest");

Upvotes: 8

Related Questions