2016-08-16 135 views
3

鉴于以下@Component类设置一些属性自动装配的bean:与注射用的Mockito和模拟

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

} 

我努力学习单元如何测试没有做一个集成测试(所以没有@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)); 
    } 
} 

...模拟不为空,但模拟类变量,因此:

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

我已经通过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()方法......也许我的测试设计是错误的)。

+5

作为一般规则,您应该只模拟具有某些功能的类。一个“价值对象” - 也就是说,一个类只是一群有getter和setter的数据 - 并不是你打算嘲笑的东西。只需使用真正的“电影”。 –

+0

是的,在写这个问题时,我已经想到了嘲笑一个非常简单的对象可能是不值得的,但是因为我对Mockito图书馆并不是很有经验,并且一般嘲笑我以为我会寻求帮助。感谢您的建议 –

回答

8

您在@Before方法中嘲笑的方式存在问题。取而代之的

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

那样做

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

这里是你必须做的;

import static org.mockito.Mockito.when;

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

@InjectMocks有助于只注入不空的对象和犯规处理值。在使用值时,我们必须明确设置需要测试的值。

如果您使用,MockitoJunitRunner不需要致电MockitoAnnotations.initMocks(this);哪一位跑步者照顾。

+0

我用'@RunWith(MockitoJUnitRunner.class)'注释了我的类。谢谢!这是官方/首选的方式来使用Mockito使用'MockitoAnnotations.initMocks(this);'? –

+0

转轮有很多优点,请查看http://stackoverflow.com/questions/15494926/initialising-mock-objects-mockito以获得更好的理解 – VinayVeluri

5

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

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

也许没有理由模拟电影。您可以使用@Spy注释,这样它将成为具有真实属性的真实对象,并且您可以同时覆盖某些方法。

嘲笑真正有用的时候建立完整的对象是硬而有很多depenencies或复杂的行为,但可能是矫枉过正,如果电影是一个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"); 
    } 

另请参阅difference between mock and spy。互联网上有很长时间的讨论需要嘲笑什么,什么不需要嘲笑。如果你仍然喜欢模拟moview,那么marians27和VinayVeluri的答案是适合你的答案。

+0

我正要回答@间谍的事情,但我认为它不是一个真正的间谍,并且使用这个注释并没有使它变得更好。问题出在代码本身之内,自动装配像“Movie”这样的类看起来并不像这样。 – g00glen00b

+1

感谢您的详细解释。在实际的代码中,我认为我确实需要模拟某个更复杂的对象,而不是使用实际的对象。因此我选择接受马里安的答案27。 这里的电影对象的自动装配仅仅是为了举例。 –