为什么80%的码农都做不了架构师?>>>   

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

写在前面:若有侵权,请发邮件by.su@qq.com告知。

本文主要是学习《Java测试驱动开发》过程中的记录,除了工具有点不一致之外,其他都是摘抄自书本。

转载者告知:如果本文被转载,但凡涉及到侵权相关事宜,转载者需负责。请知悉!

个人觉得这书相当不错,有兴趣的可以买来看看。

图书的相关链接:http://www.ituring.com.cn/book/1942

书中源码下载:https://download.csdn.net/download/gdzjsubaoya/10286870

工具:eclipse(书中用的是IntelliJ IDEA)+gradle+Junit,整个项目目录如下:

这个练习中,你将根据需求编写测试,再编写满足测试期望的代码。最后,如果有必要,将对代码进行重构。也就是书中所说的:“红灯-绿灯-重构”过程。

开发“开发井字游戏”

游戏规则:双方轮流在一个3X3的网格中画X和O,最先在水平、垂直或对角线上将自己的3个标记连起来的 玩家获胜。

需求1

我们应该首先定义边界,以及将棋子放在哪些地方非法。

可将棋子放在3X3棋盘上任何没有棋子的地方。

可将这个需求分成三个测试:

1.如果棋子放在超出了X轴边界的地方,就引发RuntimeException异常;

2.如果棋子放在超出了Y轴边界的地方,就引发RuntimeException异常;

3.如果棋子放在已经有棋子的地方,就引发RuntimeException异常;

编写第一个测试前,先简单说说如何使用JUNIT测试异常。JUnit4.7引入了一项名为规则(Rule)的功能,使用它可以做很多不同的事情,但在这里我们感兴趣的是规则Expected-Exception。

import org.junit.Test;
import org.junit.Rule;
import org.junit.rules.ExpectedException;public class TicTacToeTest {@Rulepublic ExpectedException exception = ExpectedException.none();private TicTacToe ticTacToe;}@Testpublic void whenXOutsideBoardThenRuntimeException() {exception.expect(RuntimeException.class);ticTacToe.play(5,2);}
}

1.测试

首先检查棋子是否放在3X3棋盘的边界内:

棋子放在超出X轴边界的地方时,将引发RuntimenException异常。

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;public class TicTacToeTest {@Rulepublic ExpectedException exception = ExpectedException.none();private TicTacToe ticTacToe;@Beforepublic final void before(){ticTacToe = new TicTacToe();}@Test public void whenXOutsideBoardThenRuntimeException() {exception.expect(RuntimeException.class);ticTacToe.play(5,2);}
}

这个测试中,我们指出调用方法ticTacToe.play(5,2)时,期望的结果是引发RuntimeException异常。这个测试既简短又容易,要让它通过应该也很容易:只需创建方法paly,并确保它在参数X小于1或大于3(棋盘是3X3的)时引发RuntimeException异常。你应运行这个测试3次:第一次运行时,它应该不能通过,因为此时还没有方法play;添加这个方法后,测试也应不能通过,因为它没有引发异常RuntimeException;第三次运行时应该通过,因为实现了与这个测试相关联的所有代码。

2.实现

明确什么情况下应引发异常后,实现代码编写起来应该很简单:

public class TicTacToe {public String play(int x,int y){if(x < 1 || x > 3){throw new RuntimeException("X is outside board");}}
}

3.测试

这个测试与前一个测试几乎相同,但验证的是Y轴:

 @Test public void whenYOutsideBoardThenRuntimeException() {exception.expect(RuntimeException.class);ticTacToe.play(5,2);}

4.实现

这个规范的实现几乎与前一个相同,只需在参数Y不在指定范围内时引发异常即可:

public class TicTacToe {public String play(int x,int y){if(x < 1 || x > 3){throw new RuntimeException("X is outside board");}else if(y < 1 || y >3){throw new RuntimeException("Y is outside board");}}
}

为让最后一个测试通过,添加一条“检查参数Y是否在棋盘内”的else子句。

下面编写当前需求涉及的最后一个测试。

5.测试

确定棋子在棋盘边界内后,还需确保它放在未被别的棋子占据的地方:

棋子放在被别的棋子占据的地方时,将引发RuntimeException异常。

    @Testpublic void whenOccupiedThenRuntimeException() {ticTacToe.play(2, 1);exception.expect(RuntimeException.class);ticTacToe.play(2, 1);}

这就是最后一个测试。编写 实现后,即可认为第一个需求完成了。

6.实现

为实现最后一个测试,应将既有棋子的位置存储在一个数组中。每当玩家放置新棋子时,都应确认棋子放在未占用的位置,否则引发异常:

public class TicTacToe {private Character[][] board = {{'\0','\0','\0'},{'\0','\0','\0'},{'\0','\0','\0'}};public String play(int x,int y){if(x < 1 || x > 3){throw new RuntimeException("X is outside board");}else if(y < 1 || y >3){throw new RuntimeException("Y is outside board");}if(board[x -1][y - 1] != '\0'){throw new RuntimeException("Box is occupied");}else{board[x - 1][y - 1] = 'X';}}
}

我们检查要放置棋子的位置是否被占用,如果未占用,就将相应数组元素的值从空('\0')改为被占用的(X)。注意,我们还没有记录棋子是谁(X还是O)的。

7.重构

这些代码虽然满足了测试指定的需求,但有点令人迷惑。如果有人阅读这些代码,会搞不清方法play的母的。应重构这个方法,将其中的代码放在多个方法中。重构后代码如下:

  public String play(int x,int y){checkAxis(x);checkAxis(y);setBox(x,y,lastPlayer);}private void checkAxis(int axis){if(axis < 1 || axis > 3){throw new RuntimeException("X is outside board!");}}private void setBox(int x,int y,char lastPlayer){if(board[x-1][y-1] != '\0'){throw new RuntimeException("Box is occupied");}else{board[x-1][y-1] = 'X';}}

这个重构过程中,没有改变方法play的功能,其行为与以前完全相同,但代码的可读性更强了。由于我们有覆盖了所有功能的测试,因此不用害怕重构时犯错。只要确保所有测试都通过且重构时没有引入新行为,就可以放心大胆地修改代码。

需求1.完整的源码:

测试代码:

 import org.junit.Before;import org.junit.Rule;import org.junit.Test;import org.junit.rules.ExpectedException;public class TicTacToeTest {@Rulepublic ExpectedException exception = ExpectedException.none();private TicTacToe ticTacToe;@Beforepublic final void before() {ticTacToe = new TicTacToe();}@Testpublic void whenXOutsideBoardThenRuntimeException() {exception.expect(RuntimeException.class);ticTacToe.play(5, 2);}@Testpublic void whenYOutsideBoardThenRuntimeException() {exception.expect(RuntimeException.class);ticTacToe.play(2, 5);}@Testpublic void whenOccupiedThenRuntimeException() {
-        ticTacToe.play(1, 1);ticTacToe.play(2, 1);exception.expect(RuntimeException.class);ticTacToe.play(2, 1);}}

实现代码

public class TicTacToe {private Character[][] board = {{null, null, null}, {null, null, null}, {null, null, null}};public void play(int x, int y) {if (x < 1 || x > 3) {throw new RuntimeException("X is outside board");} else if (y < 1 || y > 3) {throw new RuntimeException("Y is outside board");}if (board[x - 1][y - 1] != null) {throw new RuntimeException("Y is outside board");throw new RuntimeException("Box is occupied");} else {board[x - 1][y - 1] = 'X';}}}

需求2

现在处理轮到哪个玩家落子的问题。

需要提供一种途径,用于判断接下来该谁落子。

可将这个需求分成三个 测试:

1.玩家X先下;

2.如果上一次是X下的,接下来将轮到O下;

3.如果上一次是O下的,接下来将轮到X下。

到目前为止,我们还未使用过JUnit断言。要使用断言,需要导入org.junit.Assert类中的静态(static)方法:

import static org.junit.Assert.*;

Assert类中的方法都非常简单,它们大都以assert打头。例如,assertEquals对两个对象进行比较:assertNotEquals验证两个对象不同,而assertArrayEquals验证两个数组相同。这两个断言都有很多重载版本,因此几乎能够对任何类型的java对象进行比较。

在这里,我们需要比较两个字符,其中第一个是预期的字符,而第二个是方法nextPlayer返回的实际字符。

现在编写这些测试及其实现。

先编写测试,再编写实现代码

这样做的好处是:可 确保编写的代码是可测试的,且每行代码都有对应的测试。

通过先编写或修改测试,开发人员可在编写代码前专注于需求。这是与完成实现后再编写测试的主要差别所在。测试先行的另一个好处是,可避免原本应为质量保证的测试沦为质量检查。

1.测试

玩家X先下:

 //X玩家先下@Test public void givenFirstTurnWhenNextPlayerThenX(){assertEquals('X', ticTacToe.nextPlayer());}

应该是玩家X先下

这个测试应该是不言自明的:我们期望nextPlayer返回X。如果现在运行这个测试,将发现它都不能通过编辑,这是因为还没有方法player。我们的任务是编写方法nextPlayer,并确保它返回正确的值。

2.实现

其实根本不需要检查玩家X是否先下,因为就目前而言,只需让nextPlayer返回X就能让这个测试通过。后面的测试将要求我们修改这个方法的代码:

 public char nextPlayer(){return 'X';}

3.测试

现在需要确保让玩家轮流下。玩家X下棋后,应轮到玩家O,然后再轮到玩家X,以此类推:

    //X玩家下完,到O玩家下@Test public void givenLastTurnWhenNextPlayerThenO(){ticTacToe.play(1, 1);assertEquals('O', ticTacToe.nextPlayer());}

如果前一次是玩家X下的,接下来应轮到玩家O。

4.实现

为跟踪接下来该谁下,需要存储前一次下棋的玩家:

  private char lastPlayer = '\0';public String play(int x,int y){checkAxis(x);checkAxis(y);setBox(x,y,lastPlayer);lastPlayer = nextPlayer();} public char nextPlayer(){if(lastPlayer == 'X'){return 'O';}return 'X';}

你很可能已经进入状态。测试很小且易于编写,有了足够的经验后,编写一个测试只需一分钟甚至几秒钟;而编写实现所需的时间也差不多,甚至更短。

5.测试

我们终于可以检查玩家O下后是不是轮到玩家X了。

如果前一次是玩家O下的,接下来应轮到玩家X下。

即使什么都不用做,这个测试也能通过。因此它毫无用处,应当删除。如果编写这个测试,将发现它存在报错问题:在没有修改实现的情况下就能通过。你可以自己试一试。编写测试后,如果它在没有编写任何实现代码时就能通过,应将其删除。

此时,测试的完整代码

 import org.junit.Before;import org.junit.Rule;import org.junit.Test;import org.junit.rules.ExpectedException;import static org.junit.Assert.*;public class TicTacToeSpec {@Rulepublic ExpectedException exception = ExpectedException.none();private TicTacToe ticTacToe;@Beforepublic final void before() {ticTacToe = new TicTacToe();}@Testpublic void whenXOutsideBoardThenRuntimeException() {exception.expect(RuntimeException.class);ticTacToe.play(5, 2);}@Testpublic void whenYOutsideBoardThenRuntimeException() {exception.expect(RuntimeException.class);ticTacToe.play(2, 5);}@Testpublic void whenOccupiedThenRuntimeException() {ticTacToe.play(1, 1);ticTacToe.play(2, 1);exception.expect(RuntimeException.class);ticTacToe.play(2, 1);}@Testpublic void givenFirstTurnWhenNextPlayerThenX() {assertEquals('X', ticTacToe.nextPlayer());}@Testpublic void givenLastTurnWasXWhenNextPlayerThenO() {ticTacToe.play(1, 1);assertEquals('O', ticTacToe.nextPlayer());}}

需求3

现在考虑这个游戏的获胜规则。相比于前面的代码,这部分工作更繁琐。我们必须检查所有可能获胜的情况,只要满足其中一个,就宣布相应玩家获胜。

最先在水平、垂直或对角线上将自己的3个标记连起来的玩家获胜。

要检查同一玩家的3颗棋子是否连成了线,需要检查水平方向、垂直方向和对角线。

1.测试

下面首先定义方法play的默认返回值:

     @Testpublic void whenPlayThenNoWinner() {String actual = ticTacToe.play(1, 1);assertEquals("No winner", actual);}

如果不满足获胜条件,则无人获胜。

2.实现

默认返回值总是最容易实现的,这里也不例外:

    public String play(int x, int y) {checkAxis(x);checkAxis(y);setBox(x, y, lastPlayer);lastPlayer = nextPlayer();return "No winner";}

3.测试

指定默认结果(没有人获胜)后,处理各种获胜条件:

     @Testpublic void whenPlayAndWholeHorizontalLineThenWinner() {ticTacToe.play(1, 1); // XticTacToe.play(1, 2); // OticTacToe.play(2, 1); // XticTacToe.play(2, 2); // OString actual = ticTacToe.play(3, 1); // XassertEquals("X is the winner", actual);}

一个玩家的棋子占据整条水平线就赢了。

4.实现

为让这个测试通过,需要检查是否有水平线全被当前玩家的棋子占据。到目前为止,我们根本不关心存储到数组board中的值是什么,但现在不但要记录哪些棋盘格是空的,还需记录各个棋盘格被哪个玩家占据:

    public String play(int x, int y) {checkAxis(x);checkAxis(y);lastPlayer = nextPlayer();setBox(x, y, lastPlayer);for(int index = 0;index < 3;index++){if(board[0][index] == lastPlayer && board[1][index] == lastPlayer && board[2][index] == lastPlayer){return lastPlayer + " is the winner";    }}return "No winner";}private void setBox(int x, int y, char lastPlayer) {if (board[x - 1][y - 1] != '\0') {throw new RuntimeException("Box is occupied");} else {board[x - 1][y - 1] = lastPlayer;}}

5.重构

前面的代码能够让测试通过,完成了尽快让测试通过的使命,但并非没有改进的空间。现在我们有了确保预期行为完整性的测试,可对代码进行重构:

    private static final int SIZE = 3;public String play(int x, int y) {checkAxis(x);checkAxis(y);lastPlayer = nextPlayer();setBox(x, y, lastPlayer);if (isWin()) {return lastPlayer + " is the winner";}return "No winner";}private boolean isWin() {for (int i = 0; i < SIZE; i++) {if ((board[0][i] + board[1][i] + board[2][i]) == (lastPlayer * SIZE)) {return true;}}return false;}

重构后的解决方案看起来更好。play依然很短,很容易理解。将实现获胜逻辑的代码移到一个独立的方法中,不仅让方法play的目的变得清晰,还能让我们独立添加检查获胜条件的代码。

6.测试

我们还需检查是否有垂直线完全被某个玩家占据:

    @Testpublic void whenPlayAndWholeVerticalLineThenWinner() {ticTacToe.play(2, 1); // XticTacToe.play(1, 1); // OticTacToe.play(3, 1); // XticTacToe.play(1, 2); // OticTacToe.play(2, 2); // XString actual = ticTacToe.play(1, 3); // OassertEquals("O is the winner", actual);}

一个玩家的棋子占据整条垂直线就赢了。

7.实现

这个实现应该与前一个类似。前面在水平方向上做了检查,现在需要在垂直方向上做同样的检查:

private boolean isWin() {int playerTotal = lastPlayer * SIZE;for (int i = 0; i < SIZE; i++) {if ((board[0][i] + board[1][i] + board[2][i]) == playerTotal) {return true;} else if ((board[i][0] + board[i][1] + board[i][2]) == playerTotal) {return true;}}return false;}

8.测试

水平线和垂直线都处理后,该将注意力转向对角线了:

    @Testpublic void whenPlayAndTopBottomDiagonalLineThenWinner() {ticTacToe.play(1, 1); // XticTacToe.play(1, 2); // OticTacToe.play(2, 2); // XticTacToe.play(1, 3); // OString actual = ticTacToe.play(3, 3); // OassertEquals("X is the winner", actual);}

一个玩家的棋子占据从左上角到右下角的整条对角线就赢了。

9.实现

    private boolean isWin() {int playerTotal = lastPlayer * 3;for (int i = 0; i < SIZE; i++) {if ((board[0][i] + board[1][i] + board[2][i]) == playerTotal) {return true;} else if ((board[i][0] + board[i][1] + board[i][2]) == playerTotal) {return true;}}if (board[0][0] + board[1][1] + board[2][2] == playerTotal) {return true;}return false;}

10.测试

最后,还有最后一个可能的获胜条件需要处理:

    @Testpublic void whenPlayAndBottomTopDiagonalLineThenWinner() {ticTacToe.play(1, 3); // XticTacToe.play(1, 1); // OticTacToe.play(2, 2); // XticTacToe.play(1, 2); // OString actual = ticTacToe.play(3, 1); // OassertEquals("X is the winner", actual);}

一个玩家的棋子占据从左下角到右上角的整条对角线就赢了。

11.实现

这个测试的实现应该与前一个几乎完全相同:

    private boolean isWin() {int playerTotal = lastPlayer * 3;for (int i = 0; i < SIZE; i++) {if ((board[0][i] + board[1][i] + board[2][i]) == playerTotal) {return true;} else if ((board[i][0] + board[i][1] + board[i][2]) == playerTotal) {return true;}}if (board[0][0] + board[1][1] + board[2][2] == playerTotal) {return true;}else if (board[0][2] + board[1][1] + board[2][0] == playerTotal) {return true;}return false;}

12.重构

处理对角线时,所做的计算看起来不太好,也许重用既有的循环更合适:

    private boolean isWin(){int playerTotal = lastPlayer * 3;char diagonal1 = '\0';char diagonal2 = '\0';for(int index=0;index<SIZE;index ++){diagonal1 += board[index][index];//从左上到右下的对角线diagonal2 += board[index][SIZE-index-1];//从右下到左上的对角线if(board[0][index] + board[1][index] + board[2][index] == playerTotal){//水平线,赢return true;}else if(board[index][0] + board[index][1] + board[index][2] == playerTotal){//垂直线,赢return true;}if(diagonal1 == playerTotal || diagonal2 == playerTotal){//两条对角线return true;}}return false;}

下面处理最后一个需求。

需求4

现在缺失的唯一一项内容是如何处理平局。

所有格子都占满则为平局。

1.测试

可以通过填满棋盘的所有格子测试平局结果:

    //平局,棋盘被占满@Testpublic void whenAllBoxesAreFilledThenDraw(){ticTacToe.play(1, 1);ticTacToe.play(1, 2);ticTacToe.play(1, 3);ticTacToe.play(2, 1);ticTacToe.play(2, 3);ticTacToe.play(2, 2);ticTacToe.play(3, 1);ticTacToe.play(3, 3);String actual = ticTacToe.play(3, 2);assertEquals("The result is draw", actual);}

2.实现

检查是否为平局非常简单——只需检查是否已占满整个棋盘。为此,可遍历数组board:

   public String play(int x,int y){checkAxis(x);checkAxis(y);lastPlayer = nextPlayer();setBox(x,y,lastPlayer);if(isWin()){return lastPlayer + " is the winner";}else if(isDraw()){return "The result is draw";}else{return "No winner";}}private boolean isDraw(){for(int x=0;x<SIZE;x++){for(int y=0;y<SIZE;y++){if(board[x][y] == '\0'){return false;}}}return true;}

3.重构

   private boolean isWin(int x,int y){int playerTotal = lastPlayer * SIZE;char horizontal='\0',vertical='\0',diagonal1='\0',diagonal2 = '\0';for(int index=0;index<SIZE;index ++){horizontal += board[index][y-1];vertical += board[x-1][index];diagonal1 += board[index][index];//从左上到右下的对角线diagonal2 += board[index][SIZE-index-1];//从右下到左上的对角线if(horizontal == playerTotal || vertical == playerTotal || diagonal1 == playerTotal || diagonal2 == playerTotal){//两条对角线return true;}}return false;}//调用时isWin函数需要传对应的参数public String play(int x,int y){checkAxis(x);checkAxis(y);lastPlayer = nextPlayer();setBox(x,y,lastPlayer);if(isWin(x,y)){return lastPlayer + " is the winner";}else if(isDraw()){return "The result is draw";}else{return "No winner";}}

Git仓库中的源码:https://bitbucket.org/vfarcic/tdd-java-ch03-tic-tac-toe/branch/04-draw

或下面源码

测试源码

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.junit.Assert.*;public class TicTacToeTest{@Rulepublic ExpectedException exception = ExpectedException.none();private TicTacToe ticTacToe;@Beforepublic final void before() {ticTacToe = new TicTacToe();}@Testpublic void whenXOutsideBoardThenRuntimeException() {exception.expect(RuntimeException.class);ticTacToe.play(5, 2);}@Testpublic void whenYOutsideBoardThenRuntimeException() {exception.expect(RuntimeException.class);ticTacToe.play(2, 5);}@Testpublic void whenOccupiedThenRuntimeException() {ticTacToe.play(2, 1);exception.expect(RuntimeException.class);ticTacToe.play(2, 1);}@Testpublic void givenFirstTurnWhenNextPlayerThenX() {assertEquals('X', ticTacToe.nextPlayer());}@Testpublic void givenLastTurnWasXWhenNextPlayerThenO() {ticTacToe.play(1, 1);assertEquals('O', ticTacToe.nextPlayer());}@Testpublic void whenPlayThenNoWinner() {String actual = ticTacToe.play(1, 1);assertEquals("No winner", actual);}@Testpublic void whenPlayAndWholeHorizontalLineThenWinner() {ticTacToe.play(1, 1); // XticTacToe.play(1, 2); // OticTacToe.play(2, 1); // XticTacToe.play(2, 2); // OString actual = ticTacToe.play(3, 1); // XassertEquals("X is the winner", actual);}@Testpublic void whenPlayAndWholeVerticalLineThenWinner() {ticTacToe.play(2, 1); // XticTacToe.play(1, 1); // OticTacToe.play(3, 1); // XticTacToe.play(1, 2); // OticTacToe.play(2, 2); // XString actual = ticTacToe.play(1, 3); // OassertEquals("O is the winner", actual);}@Testpublic void whenPlayAndTopBottomDiagonalLineThenWinner() {ticTacToe.play(1, 1); // XticTacToe.play(1, 2); // OticTacToe.play(2, 2); // XticTacToe.play(1, 3); // OString actual = ticTacToe.play(3, 3); // OassertEquals("X is the winner", actual);}@Testpublic void whenPlayAndBottomTopDiagonalLineThenWinner() {ticTacToe.play(1, 3); // XticTacToe.play(1, 1); // OticTacToe.play(2, 2); // XticTacToe.play(1, 2); // OString actual = ticTacToe.play(3, 1); // OassertEquals("X is the winner", actual);}@Testpublic void whenAllBoxesAreFilledThenDraw() {ticTacToe.play(1, 1);ticTacToe.play(1, 2);ticTacToe.play(1, 3);ticTacToe.play(2, 1);ticTacToe.play(2, 3);ticTacToe.play(2, 2);ticTacToe.play(3, 1);ticTacToe.play(3, 3);String actual = ticTacToe.play(3, 2);assertEquals("The result is draw", actual);}}

实现源码:

    public class TicTacToe {private Character[][] board = {{'\0', '\0', '\0'}, {'\0', '\0', '\0'}, {'\0', '\0', '\0'}};private char lastPlayer = '\0';private static final int SIZE = 3;public String play(int x, int y) {checkAxis(x);checkAxis(y);lastPlayer = nextPlayer();setBox(x, y, lastPlayer);if (isWin(x, y)) {return lastPlayer + " is the winner";} else if (isDraw()) {return "The result is draw";} else {return "No winner";}}public char nextPlayer() {if (lastPlayer == 'X') {return 'O';}return 'X';}private void checkAxis(int axis) {if (axis < 1 || axis > SIZE) {throw new RuntimeException("X is outside board");}}private void setBox(int x, int y, char lastPlayer) {if (board[x - 1][y - 1] != '\0') {throw new RuntimeException("Box is occupied");} else {board[x - 1][y - 1] = lastPlayer;}}private boolean isWin(int x, int y) {int playerTotal = lastPlayer * SIZE;char horizontal, vertical, diagonal1, diagonal2;horizontal = vertical = diagonal1 = diagonal2 = '\0';for (int i = 0; i < SIZE; i++) {horizontal += board[i][y - 1];vertical += board[x - 1][i];diagonal1 += board[i][i];diagonal2 += board[i][SIZE - i - 1];}if (horizontal == playerTotal|| vertical == playerTotal|| diagonal1 == playerTotal|| diagonal2 == playerTotal) {return true;}return false;}private boolean isDraw() {for (int x = 0; x < SIZE; x++) {for (int y = 0; y < SIZE; y++) {if (board[x][y] == '\0') {return false;}}}return true;}}

代码覆盖率

如果需要结合JaCoCo,参考我另一篇博文,不过跟本博文中抄录的书籍无关。书中也说了JaCoCo的使用,感兴趣的可以找来阅读。

《在eclipse中使用jacoco插件对测试覆盖进行监控》

小结

我们使用“红灯-绿灯-重构”流程完成了“井字游戏”,这些示例本身都很简单,易于理解。

本章并非要深入探讨复杂的东西,而是要让你养成反复使用“红灯-绿灯-重构”流程的习惯。

你学习了如下内容:开发软件的最简单方式就是将其分成小块;设计方案脱胎于测试,而不是预先采用复杂的方法进行制定;先编写测试并确定未通过后,在着手编写实现代码;确定最后一个测试未通过后,就能肯定它是有效的(你一不小心就会犯错,编写总是能够通过的测试),要实现的功能还不存在;测试未通过后,编写其实现代码;编写实现时,力图使其尽可能简单,只要能让测试通过就行,而不试图提供完美的解决方案;不断重复这个过程,直到认为需要对代码进行重构为止;重构时不能引入任何新功能(即不改变应用程序的行为),而只是对代码进行改进,使其更容易理解和维护。

转载于:https://my.oschina.net/bysu/blog/1635549

java测试驱动开发(TDD)之《井字游戏》相关推荐

  1. java测试驱动开发_java测试驱动开发(TDD)之《遥控军舰》

    永久更新地址:https://my.oschina.net/bysu/blog/1647738 写在前面:若有侵权,请发邮件by.su@qq.com告知. 本文主要是学习<Java测试驱动开发& ...

  2. 测试驱动开发(TDD)实战小例子(JAVA版)

    我们知道,测试驱动开发(TDD)的基本思想就是在开发功能代码之前,先编写测试代码.也就是说在明确要开发某个功能后,首先思考如何对这个功能进行测试,并完成测试代码的编写,然后编写相关的代码满足这些测试用 ...

  3. 「敏捷架构」核心实践:测试驱动开发(TDD)简介

    测试驱动开发(TDD) 是一种渐进的开发方法,它结合了测试优先的开发,即在编写足够的产品代码以完成测试和重构之前编写测试.TDD的主要目标是什么?一个观点是TDD的目标是规范而不是验证(Martin, ...

  4. 测试驱动开发(TDD)的实践

    测试驱动开发(TDD)的实践 本文作者: Mr.J 本文链接: https://jiangtj.com/articles/almond/test-driven%20development/ 测试驱动开 ...

  5. Python测试驱动开发(TDD)

    Python测试驱动开发(TDD) 前言:TDD是一种敏捷开发模式,而不是测试方法. 测试很难 --- 难在坚持,一直做下去. 现在花时间编写的测试不会立即显出功效,要等到很久以后才有作用 --- 或 ...

  6. 测试驱动开发-TDD

    测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法.它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能 ...

  7. 《Java测试驱动开发》读书笔记

    目录 一.单元测试 目标 何为单元测试 为何要进行单元测试 如何进行单测-消除外部依赖 二.难以测试说明设计不佳 为何要关心设计 开发人员需要遵守的各种原则 测试人员需要遵守的原则 关于作者 Vikt ...

  8. C++ 测试驱动开发 TDD(一)

    文章目录 TDD 介绍 Soundex 算法示例介绍 增加Soundex 算法测例1 增加Soundex 算法测例2 Soundex 算法测例1 .2重构 后记 最近阅读了<C++程序设计实践与 ...

  9. 测试驱动开发-TDD(1)

    测试:作为动词,它是评估的意思:作为名词,它是导致最终是接受还是不接受的过程. 测试是相互独立的. 测试列表,就跟你生活中记录你的工作计划一样. 测试优先:你应该在什么时候编写测试呢?在你编写要被测试 ...

最新文章

  1. Android 弱网测试(小米手机切换3g和2g)
  2. R语言数据可视化 ggplot2基础2 创建单图层的散点图 创建facet
  3. cwinthread*线程指针怎么销毁结束_最新版Web服务器项目详解 01 线程同步机制封装类...
  4. JAVA IO系列----ObjectInputStream和ObjectOutputStream类
  5. 查找排序数组的最小值(js)
  6. luogu P1427 小鱼的数字游戏
  7. c++ log函数_高斯拉普拉斯算子(Laplacian of Gaussian, LoG)
  8. Linux网络编程和套接字
  9. Android studio打包apk
  10. 使用阿里云配置加速器,下载Docker镜像
  11. Portapack应用开发教程(四)GPS应用具体更改
  12. 2021年胡润中国百富榜研究报告
  13. 16S rRNA全长测序揭示中国重度污染河口细菌群落的时空动态
  14. win10连不上网,几种尝试
  15. 金融网络安全和反欺诈方法论,金融新兴技术成熟度几何?
  16. python中delta是什么意思_如何使用python计算Delta F/F?
  17. opencv学习笔记及复习(四)物体追踪及人体肤色追踪
  18. ZJM要抵御宇宙射线
  19. 基于微信小程序开发的demo
  20. 通过redis-cli批量删除多个指定模式的key

热门文章

  1. vim学习、各类插件配置与安装【转】
  2. PHP中输出本地时间
  3. Vmware VirtualCenter Server服务无法自动启动
  4. io读取一个文件再写入socket技术_Kafka必须掌握的核心技术--为什么吞吐量大、速度快?...
  5. SVN+网站服务器同步更新
  6. Visio 中如何画实现接口的连线
  7. 关于WideCharToMultiByte来解码UTF8
  8. 从0开始构建SpringCloud微服务(1)
  9. JS:The Definitive Guide JavaScript 和 XML
  10. Django例子-出版社