java測試驅動開發(TDD)之《遙控軍艦》 頂 原

永久更新地址:https://my.oschina.net/bysu/blog/1647738

寫在前面若有侵權,請發郵件[email protected]告知

本文主要是學習《Java測試驅動開發》過程中的記錄,除了工具有點不一致之外,其他都是摘抄自書本。

轉載者告知如果本文被轉載,但凡涉及到侵權相關事宜,轉載者需負責。請知悉!

個人覺得這書相當不錯,有興趣的可以買來看看。

圖書的相關鏈接:http://www.ituring.com.cn/book/1942

書中源碼下載:https://download.csdn.net/download/gdzjsubaoya/10286870

開發“遙控軍艦”

1.創建項目

書中主要是通過git導入項目到IntelliJ IDEA中,不過由於本人是用eclipse,所以我通過上面的網址下載了源碼自本地,在eclipse中創建了一個gradle項目,然後把源碼放到裏面相關目錄(沒有試過導入書中的項目會怎樣,因爲書中用的是IntelliJ IDEA + gradle+TestNG,感興趣的可以自己試一下)

工具:eclipse+gradle+TestNG,整個項目目錄如下:

2.輔助類

假設這個項目最初是由你的一位同事開發的,他是位卓越的程序員和TDD踐行者,你深信他編寫的測試有極高的代碼覆蓋率。換言之,你完全可以依賴他已做的工作。然而,這位同事還未完成這個項目就去度假了,餘下的工作將由你接手完成。他創建了所有輔助類: Direction、Location、Planet 和Point。你注意到相應的測試類也已編寫好,它們的名稱與被測試的類相同,但包含後綴Spec (如Directionspec )。使用這個後綴旨在明確這樣一點: 它們不僅用於驗證代碼,還是可執行的規範。

除這些輔助類外,還有另外兩個類: Ship (實現) 和shipspec (規範測試),你的大部分時間都將花在完善它們上。你將在ShipSpec中編寫測試,再在Sh ip類中編寫實現代碼( 與本書前面做的完全相同)。

我們知道,測試不僅提供了驗證代碼的途徑,還是可執行的文檔。因此從現在開始,我們將測試稱爲“規範”。

每次編寫規範或實現規範的代碼後,我們都將運行測試。

項目創建完畢,着手第一個需求。

3.需求1

要移動軍艦,需要知道它當前的位置;另外,還需知道軍艦面向哪個方向:北、南、東還是西。因此,第一個需求如下:

給定軍艦的起始位置(x,y)以及它面向的方向(N、S、E或W)。

處理這個需求前,先看一下可使用的輔助類。Point類存儲了座標x和y,其構造函數如下:

public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

還有枚舉類Directiion,它定義的值如下:

public enum Direction {

    NORTH(0, 'N'),
    EAST(1, 'E'),
    SOUTH(2, 'S'),
    WEST(3, 'W'),
    NONE(4, 'X');
}

最後,還有Location類,其構造函數將前述兩個類的對象作爲參數:

public Location(Point point, Direction direction) {
        this.point = point;
        this.direction = direction;
}

知道這些後,爲第一個需求編寫測試就非常容易。你應該像前一章那樣做。

請嘗試自己編寫規範,完成後再將其與本書提供的解決方案進行比較。對於實現規範的代碼,也這樣做:城市自己編寫它們,完成後再與我們提供的解決方案進行比較。

1.規範

這個需求的規範如下:

@Test
public class ShipSpec {
	public void whenInstantiatedThenLocationIsSet(){
		Location location = new Location(new Point(21, 13), Direction.NORTH);
		Ship ship = new Ship(location);
		assertEquals(ship.getLocation(), location);
	}

}

這個規範很簡單,我們 只做了這樣的檢查:傳遞給構造函數Ship的Location對象是否被存儲;能否通過獲取函數getLocation訪問它。

註解@Test

使用TestNG時,在類級指定註解@Test後,無需再指定應將哪些方法視爲測試。在這裏,所有的共有方法都被視爲TestNG測試。

2.實現

這個規範的實現非常簡單,只需將構造函數的參數賦給變量location即可:

public class Ship {
	
	private final Location location;
	
	public Ship(Location location){
		this.location = location;
	}
	
	public Location getLocation(){
		return location;
	}
}

3.重構

我們知道,需要爲每個規範實例化Ship,因此需要重構規範類,在其中添加一個用@BeforeMethod註解的方法。如下所示:

import org.testng.annotations.*;
import static org.testng.Assert.*;

@Test
public class ShipSpec {
	
	private Ship ship;
	private Location location;
	
	@BeforeMethod
	public void beforeTest(){
		location = new Location(new Point(21, 13), Direction.NORTH);
		ship = new Ship(location);
	}
	
	
	public void whenInstantiatedThenLocationIsSet(){
		/*Location location = new Location(new Point(21, 13), Direction.NORTH);
		Ship ship = new Ship(location);*/
		assertEquals(ship.getLocation(), location);
	}
}

我們沒有引入任何新的行爲,而只將部分代碼移到了用@BeforeMethod註解的方法中,以免編寫後面的規範時重複這些代碼。這樣,運行每個測試時,都將使用location爲參數實例化一個Ship對象。

4.需求2

知道軍艦在什麼地方後,下面嘗試移動它。首先,我們應該讓它能夠前進和後退。

實現讓軍艦前進和後退的命令(f和b)。

輔助類Location已包含方法forward和backward,它們實現了這項功能:

......
    public boolean forward() {
        return move(FORWARD, new Point(100, 100), new ArrayList<>());
    }
    public boolean forward(Point max) {
        return move(FORWARD, max, new ArrayList<>());
    }
    public boolean forward(Point max, List<Point> obstacles) {
        return move(FORWARD, max, obstacles);
    }

    public boolean backward() {
        return move(BACKWARD, new Point(100, 100), new ArrayList<>());
    }
    public boolean backward(Point max) {
        return move(BACKWARD, max, new ArrayList<>());
    }
    public boolean backward(Point max, List<Point> obstacles) {
        return move(BACKWARD, max, obstacles);
    }
......

1.規範

在軍艦朝北的情況下,如果我們向前移動它,結果將如何呢?其y座標將減1.如果軍艦面向東呢?其x座標應加1。

面對這樣的情況,你的第一反應應該是編寫兩個類似下面的規範:

 

 

 

 

 

 

 

 

 

 

 

未完待續...

未完待續...

未完待續...

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章