使用Mockito注入autowired bean并在mock上设置一些属性

San*_*r_M 3 java spring unit-testing mockito spring-boot

鉴于以下@Component课程:

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

}
Run Code Online (Sandbox Code Playgroud)

我正在尝试学习如何在不进行集成测试的情况下对此示例组件进行单元测试(因此测试类上没有@RunWith(SpringRunner.class)@SpringBootTest注释).

当我的测试类看起来像这样:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

... mock不是null,但mock类变量是因此:

java.lang.AssertionError: The expected name is test, but the actual name is null
Run Code Online (Sandbox Code Playgroud)

我浏览过http://www.vogella.com/tutorials/Mockito/article.html,但无法找到如何在模拟上设置类变量的示例.

如何正确模拟电影对象?更一般的是,这是测试这个MovieFinderImp类的正确方法吗?我对组件测试的灵感就是这个博客https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4

(ps:我想知道我是否应该movie.get()在这个测试类中测试方法......也许我的测试设计是错误的).

小智 8

您在@Before方法中进行模拟的方式存在问题.代替

movieMock.setTitle("test");
movieMock.setDirector("directorTest");
Run Code Online (Sandbox Code Playgroud)

这样做

Mockito.when(movieMock.getTitle()).thenReturn("test");
Mockito.when(movieMock.getDirector()).thenReturn("directorTest");
Run Code Online (Sandbox Code Playgroud)


bor*_*jab 5

模拟对象不是真正的对象,并且没有仅需要添加的模拟行为的属性.使用setter不会对他们做任何事情.除非您指定指定的行为,否则getter将返回null.这可以使用when ... thenReturn结构来完成:

when(movieMock.getTitle()).thenReturn("movie title");
Run Code Online (Sandbox Code Playgroud)

也许没有理由嘲笑电影.您可以使用@Spy注释,这样它就会成为具有真实属性的真实对象,同时您可以覆盖某些方法.

当构建完整对象很难并具有很多依赖性或复杂行为时,模拟非常有用,但如果Movie是bean,则可能是一种过度杀伤.例如:

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");
    }
Run Code Online (Sandbox Code Playgroud)

另见mock和spy之间区别.互联网上有很长时间讨论需要嘲笑什么和不需要什么.如果你仍然喜欢模拟moview,那么marians27和VinayVeluri的回答对你来说是最恰当的答案.