永久更新地址: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。
面對這樣的情況,你的第一反應應該是編寫兩個類似下面的規範:
未完待續...
未完待續...
未完待續...