TDD 之 Dojo coding
由一个人进行 setup a default practice,然后创建:
这里支持各种语言和各种测试框架,这里我们选择C++和GoogleTest
这里是选择一个题目进行联系,我们选择“网球记分规则”来练习。
成功之后,他会给每个人都分配一个动物头像,下面的三个圈,红色代表case执行失败,黄色代表编译失败,绿色代表case执行通过。
第一个创建的人,把生成的url中的http://cyber-dojo.org/kata/edit/52E43B0082?avatar=raccoon “52E43B0082” 提取出即可,这样其他人可以通过enter a practice进入:
所有人进入后,我们可以通过首页的看到所有人的一个状态:
每个人进入后,都是如下界面:
这就是我们用来练习的IDE
默认会帮你生成一个失败的case,我们点击上方的test就会失败。
于是我们需要修改case,让它变为成功状态,变为绿色。
这是我修改成功后,点击test之后,得到了绿色的小球,然后再点击绿色小球的结果,这里可以看到你每次的修改。
后面的流程基本就是写一个case,然后再实现一个逻辑的代码,然后在run。如此反复,我第一次完成的时候的代码:
先看测试代码:
#include "hiker.hpp"
#include <gtest/gtest.h>
using namespace ::testing;
TEST(Hiker, player0_0)
{
ASSERT_EQ("Love-All", score(0, 0));
}
TEST(Hiker, player1_1)
{
ASSERT_EQ("Fifteen-All", score(1, 1));
}
TEST(Hiker, player2_2)
{
ASSERT_EQ("Thirty-All", score(2, 2));
}
TEST(Hiker, player0_1)
{
ASSERT_EQ("Love-Fifteen", score(0, 1));
}
TEST(Hiker, player1_0)
{
ASSERT_EQ("Fifteen-Love", score(1, 0));
}
TEST(Hiker, player1_2)
{
ASSERT_EQ("Fifteen-Thirty", score(1, 2));
}
TEST(Hiker, player3_1)
{
ASSERT_EQ("Forty-Fifteen", score(3, 1));
}
TEST(Hiker, player0_3)
{
ASSERT_EQ("Love-Forty", score(0, 3));
}
TEST(Hiker, player4_4)
{
ASSERT_EQ("Deuce", score(4, 4));
}
TEST(Hiker, player5_4)
{
ASSERT_EQ("Advantage Player1", score(5, 4));
}
TEST(Hiker, player4_5)
{
ASSERT_EQ("Advantage Player2", score(4, 5));
}
TEST(Hiker, player6_4)
{
ASSERT_EQ("Win for Player1", score(6, 4));
}
TEST(Hiker, player4_6)
{
ASSERT_EQ("Win for Player2", score(4, 6));
}
再看业务代码
#include "hiker.hpp" string score(int p1, int p2) { string so[4]={"Love","Fifteen","Thirty","Forty"}; cout<<p1<<p2<<endl; if (p1 < 4 && p2 < 4) { if (p1==0 && p2==0) { return "Love-All"; } else if (p1==1 && p2==1) { return "Fifteen-All"; } else { return so[p1]+"-"+so[p2]; } } else { if (p1==p2) { return "Deuce"; } int cha = p1-p2; if (cha>0 && cha<2) { return "Advantage Player1"; } else if(cha>1) { return "Win for Player1"; } else if(cha<0 && cha>-2) { return "Advantage Player2"; } else if(cha<-1) { return "Win for Player2"; } } return "hello world"; }
是不是看起来逻辑好乱啊。没错我也是这样的感觉,特别想重构,一般说到重构,很多人心有余悸,没关系,我们有测试用例不用担心。
重构完成的代码和测试用例:
#include "hiker.hpp" string score(int p1, int p2) { string s_score[4]={"Love","Fifteen","Thirty","Forty"}; string s_same[4]={"Love-All","Fifteen-All","Thirty-All","Deuce"}; map<int,string> m_res; m_res.insert(pair<int, string>(1, "Advantage Player1")); m_res.insert(pair<int, string>(2, "Win for Player1")); m_res.insert(pair<int, string>(-1, "Advantage Player2")); m_res.insert(pair<int, string>(-2, "Win for Player2")); cout<<p1<<p2<<endl; // 处理相等的情况 if (p1 == p2) { if (p1 > 2) return s_same[3]; return s_same[p1]; } // 处理赢球数都在3以下的情况 if (p1 < 4 && p2 < 4) { return s_score[p1]+"-"+s_score[p2]; } // 处理赢球数都在3以上的情况 else { return m_res[p1-p2]; } return "error"; }
可以看到我一共提交了48次
大家的结果 http://cyber-dojo.org/dashboard/show/D9D934A59E:
通过这次Dojo Coding,总结如下:
1、先写case,再实现代码;
2、每实现一个功能,run一次测试代码;
3、重构的时候,每个小的单元重构一次,run一次测试代码。
这样就可以保证,你在重构完成之后,所有的功能是可以通过的。也就是TDD的思想。