test private method mock object - ntut.edu.twjykuo/train/utmock_30.pdf ·...

31
國立臺北科技大學資訊工程系郭忠義 Test Private Method Mock Object 國立臺北科技大學資訊工程系 郭忠義

Upload: others

Post on 24-Jun-2020

9 views

Category:

Documents


0 download

TRANSCRIPT

  • 國立臺北科技大學資訊工程系郭忠義

    Test Private MethodMock Object

    國立臺北科技大學資訊工程系

    郭忠義

  • 國立臺北科技大學資訊工程系郭忠義

    測試 private method 原則 單元測試

    測試客戶端如何使用物件,驗證物件行為是否符合預期。

    Object-Oriented 封裝原則 封裝變化:定義物件邊界,及外部可視介面,封裝物件內部

    變化以隔離物件內外。

    封裝細節:客戶端,不需了解物件內部資訊。

    不須特別針對private/protected method測試 Visual studio 2012刪除2010 accessor測試private method機制。 根據TDD精神,所有production code都是為通過測試案例,

    所以不會有涵蓋不到的production code。

    2

  • 國立臺北科技大學資訊工程系郭忠義

    測試 private - 充分測試 public 內聚力高原則

    Private method使用 code review即可。 充分測試 public method,即可充分測試到private method。 若無法充分測試到 private method

    over design• TDD中,private是使用refactoring的extract method產生,若測試有涵蓋不到private method的code,應刪除此code。

    private method是複雜的• 意即複雜public method、複雜的 object責任• 根據Single Responsibility Principle,重構此object,設計其它

    object負責此任務。

    3

  • 國立臺北科技大學資訊工程系郭忠義

    測試 private method 客戶滿額打折,當日累積滿額。

    4

    public class Customer {public static final int UPPER_BOUND=500;private int order[]; //訂單簡化紀錄private int getAmountOfDay() {

    int money=0;for (int i=0; i=UPPER_BOUND);

    }}

    Customer- order: int []

    - getAmountOdDay+ exceedDayLimit(int)

  • 國立臺北科技大學資訊工程系郭忠義

    測試 private – getAmountOfDay()

    5

    import static org.junit.Assert.*;import org.junit.After;import org.junit.Before;import org.junit.Test;public class CustomerTest {

    private Customer c; @Beforepublic void setUp() throws Exception {

    int [] order = {10, 20, 30, 40,50};c = new Customer(order);

    }@Afterpublic void tearDown() throws Exception {

    c=null;}@Testpublic void testExceedDayLimit() {

    assertEquals(false, c.exceedDayLimit(300));assertEquals(true, c.exceedDayLimit(1000));

    }}

  • 國立臺北科技大學資訊工程系郭忠義

    Mock – Java Mockito 下載 mockito https://code.google.com/p/mockito/

    解壓縮mockito-1.9.5.zip到C:\eclipse\mockito

    6

  • 國立臺北科技大學資訊工程系郭忠義

    Mock – Java Mockito Mockito 程式直接呼叫static function

    Foo foo = mock(Foo.class),非寫成 Foo foo = Mockito.mock(Foo.class)

    • Window - Preferences - Java - Editor - Content Assist• Add import instead of qualified name 和 Use static imports 打勾。

    7

  • 國立臺北科技大學資訊工程系郭忠義

    Mock – Java Mockito 開啟Eclipse,造出Project - TestMock 將mockito-all-1.9.jar加到the build path

    點選TestMock Project下的JRE System 按右鍵Build Path的Configure Build Path.. Properties for TestMock的Libraries的Add External JAR.. 選擇Mockito-all1.9.jar開啟

    8

  • 國立臺北科技大學資訊工程系郭忠義

    Mock – Java Mockito 開啟Eclipse,造出Project - TestMock

    造出 ITranslator interface

    造出Greeting class

    9

    public class Greeting {private ITranslator translator;public Greeting(ITranslator translator) {

    this.translator = translator; }public String sayHello(String language, String name) {

    return translator.translate("English", language, "Hello") + " " + name;}

    }

    public interface ITranslator {public abstract String translate(String fromLanguage,

    String toLanguage, String word);}

  • 國立臺北科技大學資訊工程系郭忠義

    Mock – Java Mockito Mock 架構

    10

    Setup(creation)

    Exercise(test)

    Verify

    待測程式

    (Greeting.java)Mock Object

    (Itranslator.java)

    expectations

    expectations

    InvokeI/O

    Greeting

    sayHello()

    ITranslator

    translat()

  • 國立臺北科技大學資訊工程系郭忠義

    Mock – Java Mockito 造出 mock/stub物件

    Itranslator mockTranslator = mock(Itranslator.class);

    待測物件greeting的sayHello(),呼叫mockTranslator的translate(),會產生的結果。 when(mockTranslator.translate("English", "Italian",

    "Hello")).thenReturn("Ciau");

    translate()執行時,丟出exception doThrow(new RunTimeException()).when(mockTranslator). translate();

    translate()執行時,不作任何動作 doNothing().when(mockTranslator).translate();

    驗證待測物件greeting是否呼叫所依賴物件mockTranslator 2的translate() 。 verify(mockTranslator). translate()(…) 11

  • 國立臺北科技大學資訊工程系郭忠義

    Mock – Java Mockito 造出Unit Test: File-New-Junit Unit Test

    12

    import static org.junit.Assert.*;import org.junit.After;import org.junit.Before;import org.junit.Test;import static org.mockito.Mockito.*;public class TestMock {

    @Beforepublic void setUp() throws Exception { }@Afterpublic void tearDown() throws Exception { }

  • 國立臺北科技大學資訊工程系郭忠義

    Mock – Java Mockito

    Run As – Junit Test

    13

    @Testpublic void shouldTestGreetingInItalian(){

    //setupITranslator mockTranslator = mock(ITranslator.class);Greeting greeting = new Greeting(mockTranslator);when(mockTranslator.translate(“English”, “Italian”, “Hello”)).thenReturn(“Ciau”); //executeassertEquals("Ciau Paulo", greeting.sayHello("Italian", "Paulo"));//verifyverify(mockTranslator).translate("English", "Italian", "Hello");

    }}

  • 國立臺北科技大學資訊工程系郭忠義

    Command設計模式監控系統 造一個TestServer Project

    待測類別為 Server類別,整個系統劇本• 利用Command Design Pattern設計監控系統。• 每一個門有一個跟Server註冊的ICommand物件監控。• 若門被打開則回傳IResult物件,訊息為CRITICAL,此時Server物件啟動IAlert物件警示通知。

    14

    Server+ monitor()+ addCommand(ICommand)+ removeCommand(Icommand)+ getCommandSize(): int IResult

    + OK = 1+ CRITICAL = 2+ getStatus(): int+ getMessage: String

    ICommand+ execute(): IResult

    DoorCommand+ execute(): IResult

    IDoor+ getStatus(): String

    IAlert+ sendAlert(String)

  • 國立臺北科技大學資訊工程系郭忠義

    Command設計模式監控系統 Server類別有四個method

    • addCommand:傳入ICommand物件註冊,用此物件做監控。• removeCommand:移除ICommand物件。• getCommandSize:傳回註冊的ICommand物件個數。• monitor:逐一呼叫每個向它註冊的ICommand物件之execute方法以執行監控工作。

    15

    public class Server {private List commands;private IAlert alert;public Server(IAlert i) {

    alert = i;commands = new ArrayList();

    }

  • 國立臺北科技大學資訊工程系郭忠義

    Command設計模式監控系統 Server類別有四個method

    16

    public void monitor() {IResult r=null;for (int i=0; i

  • 國立臺北科技大學資訊工程系郭忠義

    Command設計模式監控系統 IAlert介面,宣告sendAlert()發送警告。

    ICommand介面,宣告execute:監控某特定設備,監控結果以IResult物件表示。

    17

    public interface IAlert {public void sendAlert(String s);public boolean wasAlertSend();

    }

    public interface ICommand {public IResult execute();

    }

  • 國立臺北科技大學資訊工程系郭忠義

    Command設計模式監控系統 IDoor介面,宣告getStatus()獲取門的狀態開啟或關閉。

    IResult介面有兩個method• getStatus:傳回監控結果,1表示OK,2表示CRITICAL。• getMessage:傳回字串代表監控結果的敘述。

    18

    public interface IResult {public final static int OK=1;public final static int CRITICAL =2;

    public String getMessage();public int getStatus();

    }

    public interface IDoor {public String getStatus();

    }

  • 國立臺北科技大學資訊工程系郭忠義

    Command設計模式監控系統 DoorCommand類別,監控門是否被打開或關閉。

    • 透過IDoor介面判斷門的開啟或關閉狀態。– 不同sensor廠商提供IDoor介面,安裝門上監控。

    • execute呼叫_door.getStatus()檢查門的狀態– 回傳「open」代表門被打開,execute函數傳回CRITICAL的

    Result物件,將Result物件敘述設為Door is open。

    19

  • 國立臺北科技大學資訊工程系郭忠義

    五種層級Mock物件 Dummy

    不做任何事。傳入參數但不會被用到。

    Stub 根據輸入參數回傳結果。

    Spy 記錄物件的那個成員函數被呼叫,以確認與待測物件互動。

    Mock 使用Mock函式庫動態建立,設定資料提供回傳值,或預期要

    呼叫的特定成員函式。

    Fake 接近原物件但實作較簡單。

    例如Alert要傳簡訊,但先實作成存檔。 20

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:未使用Java Mockito Server類別為實作之待測類別,其餘為 mock/stub 物件。

    測試Server類別,以及與Server互動的各 stub 物件。 最小實作所有mock/stub物件

    public class TestServer {private IAlert spyAlert;private Server server;@Beforepublic void setUp() throws Exception {//server = new Server(spy);}@Afterpublic void tearDown() throws Exception {

    spyAlert=null;server=null;

    }

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:未使用Java Mockito Dummy: 不做任何事。傳入參數但不會被用到。

    TestServer.java

    public class DummyCommand implements ICommand{public IResult execute(){

    throw new RuntimeException("Unimplement");}

    }

    @Test// Using Dummy Object DummyCommandpublic void testCommandSize() {

    server = new Server(null);assertEquals(0, server.getCommandSize());//server.addCommand(null);server.addCommand(new DummyCommand());server.addCommand(new DummyCommand());assertEquals(2, server.getCommandSize());

    }

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:未使用Java Mockito Stub: 根據輸入參數回傳結果。

    public class StubClosedDoor implements IDoor{public String getStatus() { return "CLOSE"; }

    }public class StubOpenDoor implements IDoor{

    public String getStatus() { return "OPEN"; }}

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:未使用Java Mockito Stub: 根據輸入參數回傳結果。

    public class DoorCommand implements ICommand{private IDoor door;public DoorCommand(IDoor i) { door = i; }public IResult execute() {

    String s = door.getStatus();IResult r = null;if (s.startsWith("CLOSE")) {

    r = new Result(IResult.OK, "OK");System.out.println("OK~");

    } else if (s.startsWith("OPEN")){

    r = new Result(IResult.CRITICAL, "OPEN");if (r.getStatus()==IResult.CRITICAL) { System.out.println("HELP~"); }

    }return r;

    }}

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:未使用Java Mockito Stub: 根據輸入參數回傳結果。

    TestServer.java

    @Testpublic void testExecuteDoorCommandWhenDoorOpen() {

    DoorCommand doorCmd = new DoorCommand(new StubOpenDoor());IResult result = doorCmd.execute();assertEquals(Result.CRITICAL, result.getStatus());assertTrue(result.getMessage().startsWith("OPEN"));

    }@Testpublic void testExecuteDoorCommandWhenDoorClosed() {

    DoorCommand doorCmd = new DoorCommand(new StubClosedDoor());IResult result = doorCmd.execute();assertEquals(Result.OK, result.getStatus());assertTrue(result.getMessage().startsWith("OK"));

    }

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:未使用Java Mockito Spy: 記錄物件的那個method被呼叫,確認與待測物件互動

    public class SpyAlert implements IAlert{private boolean sendAlert;public SpyAlert() {

    sendAlert = false;}public void sendAlert(String s) {

    //System.out.println(s);sendAlert = true;

    }public boolean wasAlertSend() {

    return sendAlert;}

    }

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:未使用Java Mockito Spy: 記錄物件的那個method被呼叫,確認與待測物件互動

    TestServer.java@Testpublic void testMonitorSendAlertWhenDoorOpen() {

    spyAlert = new SpyAlert();server = new Server(spyAlert);server.addCommand(new DoorCommand(new StubOpenDoor()));server.monitor();assertTrue(spyAlert.wasAlertSend());

    }@Testpublic void testMonitorSendAlertWhenDoorClose() {

    spyAlert = new SpyAlert(); server = new Server(spyAlert);server.addCommand(new DoorCommand(new StubClosedDoor()));server.monitor();assertFalse(spyAlert.wasAlertSend());

    }

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:未使用Java Mockito Fake: 接近原物件但實作較簡單,例如Alert要傳簡訊,但

    先實作成存檔。

    import java.io.*;public class FakeLogAlert implements IAlert {

    private PrintWriter writer;private String fileName;private void close(Closeable c) {

    try { c.close(); }catch (IOException e) {

    e.printStackTrace(); }

    }public FakeLogAlert(String filename)

    throws IOException {fileName = filename;

    }

    public void sendAlert(String msg) {try {

    writer = new PrintWriter(fileName, "UTF-8");writer.println(msg);

    } catch (FileNotFoundException e) {e.printStackTrace();

    }catch (UnsupportedEncodingException e) {

    e.printStackTrace();}finally {

    close(writer);}

    }public boolean wasAlertSend() {

    return true;}

    }

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:未使用Java Mockito TestServer.java

    @Testpublic void testMonitorSendAlertToFakeLogAlertWhenDoorOpen() throws Exception {

    IAlert fakeAlert = new FakeLogAlert("OpenFakelog.txt");Server server = new Server(fakeAlert);server.addCommand(new DoorCommand(new StubOpenDoor()));server.monitor();// assert that the OpenFakelog.txt contains a message

    }@Testpublic void testMonitorSendAlertToFakeLogAlertWhenDoorClosed() throws Exception {

    IAlert fakeAlert = new FakeLogAlert("ClosedFakelog.txt");Server server = new Server(fakeAlert);server.addCommand(new DoorCommand(new StubClosedDoor()));server.monitor();// assert that there is no ClosedFakelog.txt

    }}

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:使用 Java Mockito 產生假的ICommand, IAlert, IResult物件,測試

    Server.minitor()與IAlert.sendAlert()的互動

    30

    public class TestServer {@Beforepublic void setUp() throws Exception { }@Afterpublic void tearDown() throws Exception { }@Testpublic void TestMockWhenWindowBroken() throws Exception {

    ICommand mockCmd = mock(ICommand.class);IAlert mockAlert = mock(IAlert.class);IResult mockResult = mock(IResult.class);Server server = new Server(mockAlert);when(mockCmd.execute()).thenReturn(mockResult);when(mockResult.getStatus()).thenReturn(IResult.CRITICAL);when(mockResult.getMessage()).thenReturn("Broken");server.addCommand(mockCmd);server.monitor();verify(mockAlert, times(1)).sendAlert("Broken");

    }

  • 國立臺北科技大學資訊工程系郭忠義

    Exercise:使用 Java Mockito 產生假的ICommand, IAlert, IResult物件,測試

    Server.minitor()與IAlert.sendAlert()的互動

    31

    @Testpublic void TestMockWhenWindowClose() throws Exception {

    ICommand mockCmd = mock(ICommand.class);IAlert mockAlert = mock(IAlert.class);IResult mockResult = mock(IResult.class);Server server = new Server(mockAlert);when(mockCmd.execute()).thenReturn(mockResult);when(mockResult.getStatus()).thenReturn(IResult.OK);server.addCommand(mockCmd);server.monitor();verify(mockAlert, times(0)).sendAlert("Close");

    }}