单体测试的六条规则
上一篇 / 下一篇 2012-07-20 09:05:22 / 个人分类:测试经验
.]4jbN.ZJB+B{0 查尔斯推荐的单体测试的六条规则:
;[WW6Xh0z^yLF]+{r0 ● 先写测试51Testing软件测试网!D5g5fo%N ?}K
51Testing软件测试网/D8E;A;F[● 从不写第一次就成功的测试51Testing软件测试网PVh8]`2FaK p
u(m*z'd1rRUnsl2Qk0 ● 从空的或不能工作的用例开始51Testing软件测试网a"Gg8e w&l*KS$Hm1O
$j,_l0I jm{6G0 ● 不要为做些可以让测试运行的琐屑的事情而担心
7k8};m*pie051Testing软件测试网?(Cw5z[D● 低耦合和可测试性是密切联系的
tT#N4nZ$T*V c051Testing软件测试网H3L7P0YAa● 使用mock对象
z(S-bEy0.r2s&PdeA e0 先写测试
1Z N:YC{ w4e@051Testing软件测试网l ]2LQ pms3Gp这是极限编程的格言,我的经验也说明这样做有效。首先编写测试以及可以让测试编译通过的足够的应用程序代码(不要太多哦!)。然后你运行测试来证明它无法工作(参照如下的第二点)。然后你写可以让那个测试通过的足够的代码(参照如下的第四点)。然后你写另外一个测试。
8I_'j1B2[g.o051Testing软件测试网R-zUK dx这种方法的好处来自你写代码的方法。你代码的每一部分都是目标驱动的。为什么我要写这行代码啊?我写是为了这样测试就可以运行。为了让测试通过我应该做些什么啊?我必须写这行代码。你会一直写一些让你程序可以完全工作的东西。51Testing软件测试网3~&QXk oF^P1[
pQZ-U P;d0 此外,先写测试意味着你在开始编码前必须决定如何让你的代码可测试。由于你在有一个覆盖代码的测试前你不会写任何代码,你就不会写任何不可测试的代码。51Testing软件测试网#R/F$cM[Z
51Testing软件测试网$l-pX p)]r从不写第一次就成功的测试
l/wN3Wx)u'XE051Testing软件测试网$KReg;^%kc/`,A写完测试后,马上运行它。它应该失败。科学的本质是证伪。写一个第一次就通过的测试不能证明任何东西。证明你的测试不是测试成功的绿色横条可以,而是从红色横条变成绿色的过程。每次我写了一个第一次就正确运行的测试,我就会怀疑它。没有代码会第一次就正确运行的。
3I6XVz-AJ A051Testing软件测试网8o:d j:Wf)a&~从空的或不能工作的用例开始
q:{9BTq7C+x4S07dU6S4b2SX0 从什么地方开始往往是一个障碍点。你如果想着针对一个方法运行的第一个测试,可以选择一些简单甚至琐碎的。有没有一个环境可以让该方法返回null,或者一个空的集合,或者一个空的数组?先测试这个用例。你的方法是不是会到数据库查询点东西?然后就可以测试如果查询一些不存在的东西会发生什么。
VDZ6\+m`(t J"G0-P@;pgF6w X0 通常从最简单的测试写起,它们可以给你一个编写更复杂交互的良好起点。它们可以让你起步。51Testing软件测试网e#YB|0_/Y
'ZwP Ro8o;phUwK0 不要为做些可以让测试运行的琐屑的事情而担心
ZD8?'O2Syxb0vORq7Bx@0K\0 这样你根据第三点写了如下测试:
%fJ] jN/[ h0public void testFindUsersByEmailNoMatch() {51Testing软件测试网D.Jp e#w"] assertEquals(51Testing软件测试网:N,n0^$mA9}.|&] W "nothing returned",51Testing软件测试网 gf(I%ct9A(H7|gC 0,51Testing软件测试网s Ef%^1F new UserRegistry().findUsersByEmail("not@in.database").length);51Testing软件测试网 XxfQ%L:h } |
zzOCDXx0 很显然,可以让这个测试通过的最小量的代码如下:
.|;z5^9r:{ |3me0public User[] findUsersByEmail(String address) {51Testing软件测试网Q:Q*wp#@Zn return new User[0];51Testing软件测试网E*@`;PJul EKy } |
写这样的就为了让测试通过的代码的自然反应可能会是,“这简直是欺骗!”。这不是欺骗,因为编写查询一个用户以确认他不在那里的代码往往会是在你开始查找用户时一种自然扩展。51Testing软件测试网v`9Z]5hm3a g
51Testing软件测试网'as~-^-uY你真正要做的事情就是通过加上简单的代码并使得测试从失败到成功来证明测试可以工作。然后,如果你写了 testFindUsersByEmailOneMatch和testFindUsersByEmailMultipleMatches,测试就是保护你 并保证你不会在一些小的用例中改变行为,保证你不会偶然抛出异常或返回null。51Testing软件测试网h*}Y5yb1x!D1edaN?
w8V}"XU:OVh0 第三点和第四点共同为你提供一个测试的基础,从而让你在开始处理重要用例时候不会忘记一些琐碎用例。51Testing软件测试网b [+ux0l+Ens
51Testing软件测试网X2lP,aY3I3@J低耦合和可测试性是密切联系的51Testing软件测试网`cy'RBK3Ky
51Testing软件测试网`^5uM+RB在你测试一个方法时候你应该让测试仅仅测试该方法。你不想事情让事情膨胀起来,或者陷入维护噩梦。例如,如果你有一个后台是数据库的应用,你就 需要有一个可以确保数据访问层可以正常工作的一套单体测试。所以你会上移一个层次并开始测试和访问层“说话”的代码。你想要控制数据库层生成的东西。你可 能还想要模拟一个数据库失败。51Testing软件测试网i|^O#{4XD
j'} i$l@.@C0 最好可以通过独立、低耦合的组件编写应用程序并使得你的测试可以生成“傻瓜”组件(参见如下的mock对象)从而让每个组件都可以相互“交流”。这样就可以使得你编写好应用的一部分的时候就可以全面测试,即使你要编写的组件会依赖于一些不存在的东西。
*x2N)U f"C tb0$B_"LFAde0 把你的应用划分成组件。将各个组件表示成相对于应用其他部分的接口,并尽量限制接口的范围。但一个组件需要给另外一个组件发消息的时候,考虑将其实施成和EventListener类似的注册/发布关系。你会发现这会使得测试更容易并很可能使得代码更容易维护。
t)m:~.[2xX:O"K"i#f5o m0r`+T!f^^*hR0 使用mock对象51Testing软件测试网"D I7e[.mW_^-e
51Testing软件测试网$Tfic(bmock对象是一中可以模仿某个特定类型的对象,实际上是一个可以记录下调用它的方法的接收端。在这里可以找到一个我通过java.lang.reflect.Proxy类写的mock对象的实现,不过我还是相信其他地方还有一些更强大的实现。51Testing软件测试网!f9?k1\"f qBv$i3c
T4@-Pzd~.L0 mock对象可以使得你能够测试隔离的组件,因为它给你一个在他们交互时一个组件对另一个组件的操作的清晰的视角。你可以在没有使用实际用户注册组件的情况下清楚地看到你所测试的组件调用了用户注册组件的“removeUser” 以及输入参数是“cmiller”。51Testing软件测试网4V k%y:|w,p3UE[-A
51Testing软件测试网G[7[4tm+IHhhmock对象的一个有效应用是在不使用EJB容器的情况下测试会话EJB 。这里有一个测试类检查了一个会话EJB是否可以在发生错误的时候可以回滚所包含的交易。注意我是如何传入一个工厂到EJB——这是一个你可以实现在测试和部署时切换实现经常使用到的方法。51Testing软件测试网 cOM+N$^l5Q
%tNn+h0y7j;{a/Q'~_051Testing软件测试网O3K3_[xco
import org.pastiche.devilfish.test.*; V1?1{-`Cd[ jr(b0import junit.framework.*; \IG+BL f6c0import java.lang.reflect.*; F3Xt!KLE,LP{0import javax.ejb.SessionContext; z-zgK i'E/b`e0public class MonkeyPlaceTest extends TestCase { 7vOm4u%r[U0 private static class FailingMonkeyFactoryHandler %u2MkUHHCd0ci |0 implements InvocationHandler { DI Q!G7_;g6R P{+_0 public Object invoke(Object proxy, Method method, Object[] args) "FciNrBp/^TM*N0 throws Exception {51Testing软件测试网J&y$X[7Z if (method.getName().equals("createMonkey"))51Testing软件测试网7RY@sG throw new MonkeyFailureException("Could not create");51Testing软件测试网#DaDeto(i*A l5E)VNm/T0 throw new UnsupportedOperationException("Didn't expect that");51Testing软件测试网6~7aQ#{q } ;P7CL&| ssR0 }51Testing软件测试网newoC$P public void testFailedCreate() throws Exception {51Testing软件测试网"}N9]:H(|/v'ek MockObjectBuilder factoryBuilder = MockObjectBuilder.getInstance(51Testing软件测试网yw P-qd+mSWr MonkeyFactory.class, 2[!\T _)V8Qo j+p0 new FailingMonkeyFactoryHandler());51Testing软件测试网1l|K'_8ne MockObjectBuilder contextBuilder = MockObjectBuilder.getInstance( } U~ `X7|+E0 SessionContext.class); ^5y({(u$l Y9rL&}0 MonkeyPlaceBean bean = new MonkeyPlaceBean(); &}&zcZOT:D0 bean.setSessionContext(51Testing软件测试网.l_X&o:t |