2016-08-16 147 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。 這裏的電影對象的自動裝配僅僅是爲了舉例。 –