>

美洲杯在线投注_2019美洲杯外围投注[投注官网]

热门关键词: 美洲杯在线投注,2019美洲杯外围投注[投注官网]

[iOS翻译]《iOS7 by Tutorials》系列:在Xcode 5里使用单

- 编辑:美洲杯在线投注 -

[iOS翻译]《iOS7 by Tutorials》系列:在Xcode 5里使用单

4.测试失败的调试

简介:

是时候追踪之前测试失败的问题了。打开GameBoard.m,找到cellStateAtColumn:andRow: 和 setCellState:forColumn:andRow: 方法,你会看到它们都调用了一个叫做checkBoundsForColumn:andRow: 的helper方法,用来检测数组边界。

单元测试是软件开发的一个重要方面。毕竟,单元测试可以帮你找到bug和崩溃原因,而程序崩溃是Apple在审查时拒绝app上架的首要原因。

头文件 GameBoard.h 里的方法注释如下:

单元测试不是万能的,但Apple把它作为开发工具包的一部分,不仅让你创作的APP更稳定,而且提供了一致、有趣的用户体验,这些都是让用户给你五星评价的源泉!iOS7提供了一个升级的单元测试框架,让你在Xcode中运行单元测试更为容易。当你完成这一章节,你将学会如何给现有app添加测试——并有可能培养出对编写测试的热爱!

// raises an NSRangeException if the column or row are out of bounds

/*

然而,如果超出边界,checkBoundsForColumn:andRow: 方法的实现抛出了一个NSGenericExpression 。通常的,你把头文件里的注释当做一个公共API规范,但在这个例子里代码和规范并不匹配,你该如何做?

本文翻译自《iOS7 by Tutorials》一书的第十一章“Unit Testing in Xcode 5”,想体会原文精髓的朋友请到Raywenderlich商店支持正版。

 一个可能性是更新注释和相关的测试,以匹配当前的实现。在这个例子里,规范里的注释看起来更有意义:一个边界检查应当遵循NSArray,并升起一个NSRangeException。

——————(博客园、新浪微博)葛布林大帝

 

*/

在GameBoard.m里,更新checkBoundsForColumn:andRow: 方法的实现如下:

 

- (void)checkBoundsForColumn:(NSInteger)column andRow:(NSInteger)row
{
if (column < 0 || column > 7 || row < 0 || row > 7)
[NSException raise:NSRangeException
format:@"row or column out of bounds"];
}

目录:

重新运行测试,这时所有测试应该能够通过了。

一、单元测试基础

自从代码不同步,注释里的规范总是有一点危险。然而,你的测试可以为注释添加双保险。自从你编写测试代码后,只要你经常运行测试,这些实现不匹配的风险会大大减小!

二、开始项目

另外,你的测试提供了一个伟大的高层概览代码,特别是遵循建议的命名规范以后。当你重新进入很久没碰过的代码后,这会非常方便——正如下一小节的内容。

三、下一步何去何从?

 

四、挑战

5.保证测试bug

附录:XCTest断言参考

一个崩溃报告刚刚进入你的app:你的一个测试人员报告你,当她运行游戏后直接点击屏幕(不点击"2 Player"或者"vs computer"按钮),程序就会崩溃。

 

你自己试一遍,就会在控制台看到下列信息:

一、单元测试基础

ReversiGame[1842:a0b] *** Terminating app due to uncaught exception 'NSRangeException', reason: 'row or column out of bounds'

在过去,Xcode引入了一个叫做OCUnit的开源单元测试框架。而在Xcode 5里面,Apple发布了他们自己的的单元测试框架,叫做XCTest。

崩溃看起来重复发生,是什么抛出了一个NSRangeException ?call stack提供了以下信息:

如果你已经熟悉OCUnit,别担心,XCTest是一个建立在OCUnit之上并且十份相似的API。从OCUnit过渡到XCTest非常简单,要做的仅仅是把STFail替换为XCTFail、STAssert替换为XCTAssert等等诸如此类。如果你已经熟悉这些基础,可以直接跳到下一节。

2 CoreFoundation  [NSException raise:format:]   139
3 ReversiGame -[GameBoard checkBoundsForColumn:andRow:]   142 4 ReversiGame -[GameBoard cellStateAtColumn:andRow:]   76
5 ReversiGame -[ReversiBoard flipOpponentCountersForColumn: andRow:withNavigationFunction:toState:]   281
6 ReversiGame -[ReversiBoard makeMoveToColumn:andRow:]   245 7 ReversiGame -[BoardSquare cellTapped:]   192

 

从下往上读:

 

第7、6行  tap触发代码用来处理玩家的移动

1.高层次概述

第5行 游戏逻辑检查是否有对手的棋子被包围并且翻转

 

第4、3行 代码然后调用cellStateAtColumn:andRow: 和

 单元测试有四个层级。从上到下,它们分别是:

checkBoundsForColumn:andRow:

篮球世界杯在线投注, 

第2行 底层框架报出一个越界异常

  • 测试套件(Test suite)
    • 这是项目里测试的全部集合。在Xcode里,测试套件被设置为一个单独的build target。
  • 测试用例类(Test case classes)
    • 正如你可能所期待的,在一个面向对象体系中,测试被整合到类里。在你的app里,每个测试类通常对应一个单独类。例如,DeveloperTests类应该对应Developer类的测试。
  • 测试用例方法(Test case methods)
    • 每个测试类包含多种方法,用来测试类的各种功能。就像一个方法或函数应该既精简又实用,每个测试用例应该测试一个特定的结果——并且完全测试。
  • 断言(Assertions)
    • 断言检查对应预期结果的具体条件。如果条件不符合预期结果, Xcode会报错指出断言失败。例如,可以断言你的Developer 类响应“writeKillerApp: message”;如果它没有,断言失败,Xcode报错。

 

 

想了解更多调试崩溃的信息?看这里

 

My App Crashed, Now What?
http://www.raywenderlich.com/10209/my-app-crashed-now-what-part-1
Demystifying iOS Application Crash Logs
http://www.raywenderlich.com/23704/demystifying-ios-application-crash-logs

理论很美好,但有时举例会更容易阐述事物。用Empty Application 模板创建一个新项目,命名为EmptyApp。 Xcode模板会自动包含一个叫做EmptyAppTests的test target,添加到EmptyApp的 app target里,如下图:

这是一个测试这些崩溃条件的绝好机会。

篮球世界杯在线投注 1

你的新测试不止要修复这个问题,而且要作为一个回归测试确保这个bug保持修复。没有什么比修复一个bug后,数月之后添加新功能或重构代码时再遇到相同的bug更让人不爽的了。

 

 

注意测试用例类包含了一个没有关联头文件的.m文件,打开EmptyAppTests.m看看第一个测试用例的源代码。

6.决定什么需要测试

测试方法必须以单词test开始,以便test runner能找到它们。在你的示例项目里,测试类包含了一个测试方法,叫做testExample

你知道你需要测试——但应该测试什么?

 

ReversiBoard 是GameBoard类的通用实现,所以从这里开始故障排除工作是有意义的。

setUptearDown方法就像守护在测试用例周围的卫兵一样。

 

把所有对象的程序设置代码或重复性代码放到setUp里,使测试用例方法保持清爽、高效。

使用iOSCocoa TouchObjective-C test case class 模板创建一个新类,命名为ReversiBoardTests, 继承自XCtestCase。

类似的,关闭文件句柄或取消挂起网络请求等清理活动的方法应该放到tearDown里。

在开始之前,删除模板文件的testExample方法,然后在ReversiBoardsTests.m 里导入头文件:

 

#import "ReversiBoard.h"

Test runner 会依次调用setUp、testExample和tearDown方法。如果你申明了第二个测试方法testSecondExample,Test runner会依次调用setUp、testSecondExample,最后是tearDown方法。如果你有多个测试方法,setUp和tearDown会在一个测试环节调用多次——每经过一个测试用例方法调用一次!

 

 

ReversiBoardsTests.m 里改变@interface 如下:

这个故事的寓意是不要放任何处理太慢或处理频繁的东西到setUp或tearDown方法里**——这会让你运行测试套件时面临漫长的等待**!

@interface ReversiBoardTests : XCTestCase { ReversiBoard *_reversiBoard;
}

 

添加一个_reversiBoard 实例变量意味着你不用在每个测试方法里反复实例化。

 

然后修改setUp方法如下:

 

- (void)setUp {
[super setUp];
_reversiBoard = [[ReversiBoard alloc] init];
}

2.创建你的第一个测试

 

 

7.测试否定

 

在之前的编写的测试中,异常的存在是预期的结果。这一次,异常并没有在你的测试基础上出现。

testExample方法只有一个叫做XCTFail的语句,正如它名字里暗示的:总是会失败。这个语句不是非常有用,你可以写一个比它更好的!删除testExample方法,并添加如下方法:

添加这些方法到ReversiBoardsTests.m 

- (void)test_addition_twoPlusTwo_isFour 
{
  XCTAssert(2   2 == 4, @"2   2 should be 4 but %d was returned instead", 2 2);
}

- (void)test_makeMove_inPreGameState_nothingHappens {
[_reversiBoard setToPreGameState];
XCTAssertNoThrowSpecificNamed(
[_reversiBoard makeMoveToColumn:3 andRow:3],
NSException,
NSRangeException,
@"Making a move in the pre-game state should do nothing");
}

测试用例的一个常用命名标准是:unitOfWork_stateUnderTest_expectedBehavior (工作单元_测试状态_预期行为)。

上面的代码中,测试设置游戏前的状态。也就是说,玩家作出对战AI还是对战其他玩家选择之前的状态。这个测试模拟了一进入游戏就点击棋盘的动作。

在这个例子里,被测试的工作单元是加法,测试状态是2 2,预期行为是结果为4。

XCTAssertNoThrowSpecificNamed 断言与XCTAssertThrowsSpecificNamed 刚好相反。如果指定的异常被抛出,上面的测试会失败;如果指定的异常没被抛出,测试会通过。

所有XCTest断言都有前缀XCT。XCTAssert是可用于单元测试的简单断言,第一个参数是预评估为ture的表达式,当断言失败时,其后NSLog风格的参数会显示一条消息。

你还没有修复bug,所以现在运行代码将会失败——不过这是件好事,在修复bug之前编写测试意味着你拥有重现bug的测试能力。

 

 

确保项目的当前target为iPhone模拟器,通过窗口顶部目录的Product -> Test(Command-U)来运行测试,模拟器会启动并执行测试套件。如果通知处于激活状态,你会看到下列确认消息:

Command U 运行测试,你会看到如下信息:

篮球世界杯在线投注 2

test failure: -[ReversiBoardTests test_makeMove_inPreGameState_nothingHappens] failed: (([_reversiBoard makeMoveToColumn:3 andRow:3]) does not throw <NSException, "NSRangeException">) failed

 

 

 

8.校正代码

为了证实第一个单元测试成功,切换到Test Navigator,箭头指出了它:

打开 ReversiBoard.m 然后找到 makeMoveToColumn:andRow: 方法。

篮球世界杯在线投注 3

思考一下如何修正这个特定的bug。只有用户选择了游戏模式之后才可以移动,这是很有意义的。这样一想,游戏前和游戏后的游戏逻辑没有什么不同。

哈哈!翠绿色的小勾旁边显示出了你的单元测试。

幸运的是,这里有一个属性指定当前的游戏状态:gameState.

你还可以看到边框空白处菱形图标旁的代码,如下所示:

 

篮球世界杯在线投注 4

添加以下代码到makeMoveToColumn:andRow: 方法的顶部:

这些图标展示关联测试代码的状态:

if ([self gameState] != GameStateOn) return;

@implementation旁的绿色小勾表示这个类测试通过,test_addition_twoPlusTwo_isFour旁的绿色小勾表示这个方法测试通过。

这个条件检测了当前的游戏状态。如果状态不是GameStateOn——说明游戏不在运行中——方法立即终止。

同时,这些图标也是按钮:

运行app,测试一下在选择游戏模式之前点击棋盘,是否崩溃?

点击@implementation旁的图标将会运行这个类的所有测试,点击其他测试方法旁的图标则会运行该测试方法,试一试吧!

 

 

最后,Command U 运行测试,Test Navigator应该显示绿色小勾,bug被碾碎了!

现在你已经对测试的概念和执行有了初步了解,是时候开始本章的示例项目了——测试开始!

探索风格的测试只用包含明显问题的代码,然而回归风格的测试则可以为经常修复某个问题提供了保障。

 

修复每个bug不止是让你的代码更健康,同时让你有更多时间思考你的单元测试。

二、开始项目

 

本章的剩余部分你将使用一个名为Reversi的黑白棋游戏项目,规则:两个玩家,分别代表白方和黑方,轮流在8x8棋盘上落子。通过包围对方棋子来吃掉它,游戏结束时棋子最多的为胜者。

本文由计算机教程发布,转载请注明来源:[iOS翻译]《iOS7 by Tutorials》系列:在Xcode 5里使用单