2015年7月7日 星期二

JUnit與Mockito

在程式進行測試時,常常會使用JUnit進行測試,其在NetBeans中已有內建。

而Mockito是一個需要下載的工具(下載jar後引入library中使用),它可以很方便地讓我們模擬還未撰寫完成的模組單元(類別還未實作完的類別),用模擬的單元來進行測試。

在單元測試中,我們有時會碰到一個單元要使用另一個單元的情況,例如單元A要使用單元B,而我們想在假設單元B一定正確的前提下測試單元A、或者是單元B根本就還沒實作完成(不過方法介面等要先出來),這時我們就會需要在單元A的測試中模擬出一個虛擬的單元B幫助測試。

在這裡"JUnit + Mockito 單元測試"有不錯的示例與說明可以參考。

我們在這裡演示一個簡單的例子:

有一個有兩個類別,Singer與Song,其中在Singer的建構子中我們注入一個Song給它,並在Singer的sing()方法中呼叫Song的getLyrics(),sing()方去會回傳從getLyrics()得到的lyrics,而lyrics的值就是Song建構子要傳入的參數,兩個類別的內容如下所示。



Singer:
public class Singer {

    private Song song;
    private String lyricsPrefix = "I'm Hugo and I'm singing, ";
    
   

    //注入Song到Singer中
    public Singer(Song song) {
        this.song = song;
    }
    
    //獲取lyricsPrefix
    public String getLyricsPrefix(){
        return lyricsPrefix;
    }
    
    //限制無無參數建構子
    private Singer() {

    }

    public String sing() {
        //將song.getLyrics()加上前最綴lyricsPrefix並傳回
        String lyrics = lyricsPrefix + song.getLyrics();
        return lyrics;
    }
}

Song:
public class Song {

    private String lyrics;

    public Song(String lyrics) {
        this.lyrics = lyrics;
    }

    public String getLyrics() {
        return lyrics;
    }
}


正常情況下,即單元B如上述已經實作好的話,我們可以如下用JUnit去測試它,重點在testSinger()測試方法:
import junit.framework.Assert;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class JUnitTest {
    
    public JUnitTest() {
    }
    
    //在所有測試執行前執行
    @BeforeClass
    public static void setUpClass() {
    }
    
    //在所有測試執行後執行
    @AfterClass
    public static void tearDownClass() {
    }
    
    //在單個測試前執行
    @Before
    public void setUp() {
    }
    
    //在單個測試後執行
    @After
    public void tearDown() {
    }
    
    @Test
    public void testSinger(){
        String lyrics = "La...,La...,La...";
        Song song = new Song(lyrics);
        Singer singer = new Singer(song);
        
        assertEquals(singer.getLyricsPrefix()+lyrics, singer.sing());
    }

}


我們想要確認是否Singer的sing()方法會把我們送進Song的建構子的參數值加上前綴(可用Singer.getLyricsPrefix()得到)後傳回。但如果假設今天Song的內容還未實作或其實有錯,而我們只想確定如果假設Song沒有錯的情況下Singer的類別能不能通過測試,即是說我們現在測試上層的Singer有沒有實作正確,這時就可以使用Mockito的mock()方法來建立一個模擬的Song,並且設定好它的模擬動作。測試的程式碼如下所示:
import junit.framework.Assert;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class JUnitTest {
    
    public JUnitTest() {
    }
    
    //在所有測試執行前執行
    @BeforeClass
    public static void setUpClass() {
    }
    
    //在所有測試執行後執行
    @AfterClass
    public static void tearDownClass() {
    }
    
    //在單個測試前執行
    @Before
    public void setUp() {
    }
    
    //在單個測試後執行
    @After
    public void tearDown() {
    }
    
    @Test
    public void testSinger(){
        String lyrics = "La...,La...,La...";
        //Song song = new Song(lyrics);
        //模擬Song
        Song mockSong = mock(Song.class);   
        //設定模擬的Song的動作:如果被呼叫getLyrics()方法
        //就回傳lyrics
        when(mockSong.getLyrics()).thenReturn(lyrics);
        Singer singer = new Singer(mockSong);
        //假設Song的getLyrics()正確的情況下,
        //Singer的sing()是否正常運作
        assertEquals(singer.getLyricsPrefix()+lyrics, singer.sing());
     }
}


以上的程式碼假設了Song的getLyrics()正常運作下,Singer的sing()傳回的應該是Singer.getLyricsPrefix加上Song.getLyrics(),我們可以試著把Song的getLyrics()實作內容改成錯誤的應該可以發現還是能通過測試,因為我們使用的是模擬的mockSong而非真正的Song。

沒有留言 :

張貼留言