TDD的iOS开发初步以及Kiwi使用入门

发表于:2014-2-24 11:06

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:OneV\\\'s Den    来源:51Testing软件测试网采编

  接下来我们再为这个context添加一个测试例,用来测试初始状况时栈是否为空。因为我们使用了一个Array来作为存储容器,根据我们之前用过的equal方法,我们很容易想到下面这样的测试代码
  it(@"should equal contains 0 element", ^{
  [[theValue([stack.numbers count]) should] equal:theValue(0)];
  });
  这段测试在逻辑上没有太大问题,但是有非常多值得改进的地方。首先如果我们需要将原来写在Extension里的numbers暴露到头文件中,这对于类的封装是一种破坏,对于这个,一种常见的做法是只暴露一个-count方法,让其返回numbers的元素个数,从而保证numbers的私有性。另外对于取值和转换,其实theValue的存在在一定程度上是破坏了测试可读性的,我们可以想办法改善一下,比如对于0的来说,我们有beZero这样的期望可以使用。简单改写以后,这个VVStack.h和这个测试可以变成这个样子:
//VVStack.h
//...
- (NSUInteger)count;
//...
//VVStack.m
//...
- (NSUInteger)count {
return [self.numbers count];
}
//...
it(@"should equal contains 0 element", ^{
[[theValue([stack count]) should] beZero];
});
  更进一步地,对于一个collection来说,Kiwi有一些特殊处理,比如have和haveCountOf系列的期望。如果测试的对象实现了-count方法的话,我们就可以使用这一系列期望来写出更好的测试语句。比如上面的测试还可以进一步写成
  it(@"should equal contains 0 element", ^{
  [[stack should] haveCountOf:0];
  });
  在这种情况下,我们并没有显式地调用VVStack的-count方法,所以我们可以在头文件中将其删掉。但是我们需要保留这个方法的实现,因为测试时是需要这个方法的。如果测试对象不能响应count方法的话,如你所料,测试时会扔一个unrecognized selector的错。Kiwi的内部实现是一个大量依赖了一个个行为Matcher和objc的消息转发,对objcruntime特性比较熟悉,并想更深入的朋友不放可以看看Kiwi的源码,写得相当漂亮。
  其实对于这个测试,我们还可以写出更漂亮的版本,像这样:
  it(@"should equal contains 0 element", ^{
  [[stack should] beEmpty];
  });
  好了。关于空栈这个情景下的测试感觉差不多了。我们继续用TDD的思想来完善VVStack类吧。栈的话,我们当然需要能够-pop,也就是说在(Given)给定一个栈时,(When)当栈中有元素的时候,(Then)我们可以pop它,并且得到栈顶元素。我们新建一个context,然后按照这个思路书写行为描述(测试):
context(@"when new created and pushed 4.6", ^{
__block VVStack *stack = nil;
beforeEach(^{
stack = [VVStack new];
[stack push:4.6];
});
afterEach(^{
stack = nil;
});
it(@"can be poped and the value equals 4.6", ^{
[[theValue([stack pop]) should] equal:theValue(4.6)];
});
it(@"should contains 0 element after pop", ^{
[stack pop];
[[stack should] beEmpty];
});
});
  完成了测试书写后,我们开始按照设计填写产品代码。在VVStack.h中完成申明,并在.m中加入相应实现。
  - (double)pop {
  double result = [self top];
  [self.numbers removeLastObject];
  return result;
  }
  很简单吧。而且因为有测试的保证,我们在提供像Stack这样的基础类时,就不需要等到或者在真实的环境中检测了。因为在被别人使用之前,我们自己的测试代码已经能够保证它的正确性了。VVStack剩余的最后一个小问题是,在栈是空的时候,我们执行pop操作时应该给出一个错误,用以提示空栈无法pop。虽然在objc中异常并不常见,但是在这个情景下是抛异常的好时机,也符合一般C语言对于出空栈的行为。我们可以在之前的“when created”上下文中加入一个期望:
  it(@"should raise a exception when pop", ^{
  [[theBlock(^{
  [stack pop];
  }) should] raiseWithName:@"VVStackPopEmptyException"];
  });
  和theValue配合标量值类似,theBlock也是Kiwi中的一个转换语法,用来将一段程序转换为相应的matcher,使其可以被施加期望。这里我们期望空的Stack在被pop时抛出一个叫做”VVStackPopEmptyException”的异常。我们可以重构pop方法,在栈为空时给一个异常:
- (double)pop {
if ([self count] == 0) {
[NSException raise:@"VVStackPopEmptyException" format:@"Can not pop an empty stack."];
}
double result = [self top];
[self.numbers removeLastObject];
return result;
}
  进一步的Kiwi
  VVStack的测试和实现就到这里吧,根据这套测试,您可以使用自己的实现来轻易地重构这个类,而不必担心破坏它的公共接口的行为。如果需要添加新的功能或者修正已有bug的时候,我们也可以通过添加或者修改相应的测试,来确保正确性。我将会在下一篇博文中继续介绍Kiwi,看看Kiwi在异步测试和mock/stub的使用和表现如何。Kiwi现在还在比较快速的发展中,官方repo的wiki上有一些不错的资料和文档,可以参考。VVStack的项目代码可以在这个repo上找到,可以作为参考。
55/5<12345
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号