- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
注意:我在标题中添加了 kif 只是为了搜索索引的目的,考虑到大部分答案都是讨论它
我正在寻找适用于 iOS 的类似 selenium 的东西,基本上是一个测试自动化/单元测试框架,可以多次运行某个 UI 场景直到它崩溃,这将帮助我缩小导致 UI 错误的原因很少和随机发生。
(顺便说一下,我已经对数据源/表交互的每一行代码进行了 NSLogged,并花了数小时分析潜在原因......但没有发现任何结论......同样,这个错误很少发生)。
我看了一些 unit testing frameworks in iOS ,但他们似乎有这么多。我不确定该选哪个。另外,我对 selenium 的引用是基于推测,因为我曾与过去在大型 Web 项目中使用过 Selenium 的 QA 人员合作(我假设 iOS 一定有类似的东西)。
既然我是一个 iOS 项目的单人团队,我将不得不戴上 QA 帽子并找出这个错误。
我遇到了一个典型的错误,当 UITableView 中插入的实际行数与数据源委托(delegate)返回的行数之间存在差异时,就会发生这种错误。这是错误信息:
*** Assertion failure in -[UITableView
_endCellAnimationsWithContext:] Exception in insertRows: Invalid
update: invalid number of rows in section 0.
The number of rows contained in an existing section after the update (2) must be equal to
the number of rows contained in that section before the update (2),
plus or minus the number of rows inserted or deleted from that section
(1 inserted, 0 deleted) and plus or minus the number of rows moved
into or out of that section (0 moved in, 0 moved out).
我单击一个 UITableViewCell
,它会将我带到另一个 UITableView
。有时它有效
有时(很少)它不会(出现上述错误):
最佳答案
更新:..我在分隔符后的底部添加了关于 KIF 2.0 的示例代码..对于那些对 KIF 比我面临的具体问题更感兴趣的人:
经过一些研究和试验..我将我的选择范围缩小到两个测试自动化库: Frank和 KIF .我最终决定在借用 cucumber's 时使用 KIF
小 cucumber syntax描述我的单元测试。
我选择 KIF
(而不是 Frank
)的原因是 KIF
是 100% 基于 obj-c 的,而不是使用 ruby 作为Frank
也是如此。所以设置更简单,更适用于我狭窄的测试用例要求。话虽如此,我承认如果我的应用程序更复杂(即使用来自多个服务器的输入等),Frank
会更有用。你可以看到这个 excellent 的最后一个季度演示文稿以了解有关 KIF、Frank 和其他自动化测试框架(包括 Apple 自己的框架)的优缺点的更多信息 UI Automation .
在使用 KIF 后,我发现了导致上述错误的错误,并且我可以 100% 地使用 KIF 重现它!它很少发生的原因是因为它只发生在我非常快地点击屏幕时..而且因为 KIF 自动执行这些步骤..它以非常快的速度完成它们..这暴露了错误:).
下面是我用于测试的代码示例。这只是为了让您快速了解 KIF(和 Gherkin)可以为您做什么:
在一个文件中我指定了我想要运行的场景:
- (void)initializeScenarios;
{
[self addScenario:[KIFTestScenario scenarioToCompleteSignInAndLoadInbox]];
[self addScenario:[KIFTestScenario scenarioToFillAttachmentsWithData]];
[self addScenario:[KIFTestScenario scenarioToViewAndLoadFileBucket]];
[self addScenario:[KIFTestScenario scenarioToViewAndLoadFileBucketSubView]];
}
每个场景映射到步骤(要了解更多关于基于测试驱动程序开发的 gherkin 语法和行为驱动开发,我强烈建议阅读这本关于 cucumber 的优秀书籍):
/* @given the application is at a fresh state
@and the user already has an imap email account with a valid username/pwd
@then the user can successfully log in
@and the inbox view will be loaded
@and the inbox will get loaded with the latest batch of emails in the user inbox
*/
+ (id)scenarioToCompleteSignInAndLoadInbox
{
KIFTestScenario *scenario =
[KIFTestScenario scenarioWithDescription:@"Test that a user
can successfully log in."];
[scenario addStepsFromArray:[KIFTestStep stepsCompleteSignInAndLoadInbox]];
return scenario;
}
/* @given that the user is already signed in
@and the user has already downloaded their folders
@then the user can click on the folders view
@and the user can click on the 'attachments' remote folder
@and the latest batch from the 'attachments' remote folder will download
*/
+ (id)scenarioToFillAttachmentsWithData {
KIFTestScenario* scenario =
[KIFTestScenario scenarioWithDescription:@"Test that we can view the
attachments folder and fill
it with data."];
[scenario addStepsFromArray:[KIFTestStep stepsToFillAttachmentsWithData]];
return scenario;
}
/* @given that the user is already signed in
@and the user has already downloaded their folders
@and the user has already downloaded attachments
@then the user can click on inbox menu button
@and the user can click on folder list menu button
@and the user can click on the file bucket icon (on the account list view)
@and the data for the file bucket is fetched from the dbase
@and the file bucket view displayes the attachments
*/
+ (id)scenarioToViewAndLoadFileBucket {
KIFTestScenario *scenario =
[KIFTestScenario scenarioWithDescription:@"Test that a user can successfully
view and load
file bucket parent view"];
[scenario addStepsFromArray:[KIFTestStep stepsToViewAndLoadFileBucketPage]];
return scenario;
}
/* @given that the user is already signed in
@and the user has already downloaded their folders
@and the user has already downloaded attachments
@and the user has already opened file bucket view
@then the user can click on a random row in the file bucket view table
@and the subview will retrieve data from the dbase pertaining to that row
@and the subview will display the data in the uitableview
*/
+ (id)scenarioToViewAndLoadFileBucketSubView {
KIFTestScenario *scenario =
[KIFTestScenario scenarioWithDescription:@"Test that a user can successfully
view and load filet
bucket sub view"];
[scenario addStepsFromArray:[KIFTestStep stepsToViewAndLoadFileBucketSubPage]];
return scenario;
}
步骤是使用 KIF 的 UI 自动化方法定义的(这只是一个示例):
// this step assumes there is an attachment folder that contains emails with attachments
+ (NSArray *)stepsToFillAttachmentsWithData {
NSMutableArray* steps = [@[] mutableCopy];
[steps addObject:
[KIFTestStep stepToTapViewWithAccessibilityLabel:@"InboxMenuButton"]];
NSIndexPath* indexPath =
[NSIndexPath indexPathForRow:remoteAttachmentFolderNumber inSection:0];
KIFTestStep* tapAttachmentRowStep =
[KIFTestStep stepToTapRowInTableViewWithAccessibilityLabel:
@"attachments" atIndexPath:indexPath];
[steps addObject:[KIFTestStep stepToWaitForNotificationName:
(NSString *)kBeganSyncingOlderEmails object:nil
whileExecutingStep:tapAttachmentRowStep]];
[steps addObject:tapAttachmentRowStep];
[steps addObject:
[KIFTestStep stepToWaitForViewWithAccessibilityLabel:@"attachments"]];
KIFTestStep *fillingInboxStep =
[KIFTestStep stepToWaitForNotificationName:
(NSString *)kOldMailBatchDelivered object:nil];
[fillingInboxStep setTimeout:kSpecialTimeoutForLongTests];
[steps addObject:fillingInboxStep];
return steps;
}
KIF 2.0 示例代码:KIF 2.0 使用 Xcode 5 的全新 test navigator .. 这比 KIF 1.0 所做的巨大改进..现在您的测试感觉比过去更加有机和自然..(即实时进行..而不是创建场景在未来运行等)。您甚至可以使用播放按钮等来测试每个。您应该尝试一下。
这里有一些例子(同样使用 gherkin 语法):
#import <KIF/KIF.h>
#import "KIFUITestActor+EXAdditions.h"
#import "KIFUITestActor+UserRegistration.h"
@interface LoginTests : KIFTestCase
@end
@implementation LoginTests
- (void)testReset {
[tester flushDbase];
[tester reset];
}
/* @given that the app is in a fresh clean state
@and that no one has ever registered with the server
@then the user can register their themselves with the server
@and immediately start with the rider's map
@and their location on the map shows
*/
- (void)testRegistration
{
[tester flushDbase];
[tester reset];
[tester singleUserRegistration];
[tester showUserCurrentLocationOnMap];
}
/* @given that the user has already registered with the server
@and the user is not currently logged in
@then the user can login using their user name and password
@and immediately start with the rider's map
@and their location on the map shows
*/
- (void)testSuccessfulLogin
{
[tester reset];
[tester login];
[tester showUserCurrentLocationOnMap];
}
/* @given that the user has already registered
@and that the user is already logged in before app launch
@then the user starts on the map view with the location visible
@and the button prompts them to set pick up location
*/
- (void)testStartOfApplication {
[tester showUserCurrentLocationOnMap];
[tester showsPickUpButton];
}
@end
下面是类别文件中一些测试用例的实现:
- (void)reset
{
[self runBlock:^KIFTestStepResult(NSError **error) {
BOOL successfulReset = YES;
// Do the actual reset for your app. Set successfulReset = NO if it fails.
AppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate resetApp];
KIFTestCondition(successfulReset, error, @"Failed to reset some part of the application.");
return KIFTestStepResultSuccess;
}];
}
- (void)flushDbase {
[self runBlock:^KIFTestStepResult(NSError **error){
NSURL *url = [NSURL URLWithString:@"http://randomdomain.com/flush_db"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSError *connectionError = nil;
BOOL databaseFlushSucceeded = YES;
NSURLResponse *response;
NSData *resultData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&connectionError];
if (!resultData) {
databaseFlushSucceeded = NO;
KIFTestCondition(databaseFlushSucceeded, error, @"failed to connect to server!");
}
if (connectionError) {
databaseFlushSucceeded = NO;
KIFTestCondition(databaseFlushSucceeded, error, [NSString stringWithFormat:@"connection failed. Error: %@", [connectionError localizedDescription]]);
}
return KIFTestStepResultSuccess;
}];
}
- (void)navigateToLoginPage
{
[self tapViewWithAccessibilityLabel:@"login email"];
}
- (void)returnToLoggedOutHomeScreen
{
[self tapViewWithAccessibilityLabel:@"Logout"];
[self tapViewWithAccessibilityLabel:@"Logout"]; // Dismiss alert.
}
关于objective-c - KIF:如何自动运行/压力测试 iOS 应用程序以找出罕见的 UI 错误的原因?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17229460/
找出/计算符号的宽度 panel.add(textfield,BorderLayout.SOUTH); system.out.println(textfield.getWidth()); System
嘿,所以我正在制作一个因式分解程序,我想知道是否有人可以给我任何想法,让我知道如何找到一个有效的方法来找到两个数字乘以指定数字的倍数,以及添加到指定数字。 例如我可能有 (a)(b) = 6 a +
我以以下方式将 GWT 方法导出到 native javascript: public class FaceBookGalleryEntryPoint implements EntryPoint {
通常,当您在 Web 上找到 Silverlight 代码示例时,它可能只包含一段代码,而不是使其工作所需的完整代码集。当我试图确定在 xaml 文件顶部使用什么命名空间和/或程序集声明时,这让我感到
我对 Dojo 工具包有点陌生。有些问题我想得到启发(我用谷歌搜索,但没有得到任何合适且令人满意的答案) 我已经在运行的应用程序(由另一个软件开发人员开发)中有一个 dojo.js(也许是下载的未压缩
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: How to detect which row [ tr ] is clicked? 我有一个这样的表:
我目前正在尝试找出特定应用程序使用的数据保护类别。 我的第一个方法是使用未加密的 iTunes 备份来确定所使用的保护类别。我用过this提取备份。但现在我要陷入困境了。 此外,我不太确定 iTune
我有一个 NSRangeException 错误,该错误并不总是发生(尤其是在调试时)。它是随机出现的,我无法弄清楚它来自哪里。我有很多数组操作,因此很难以这种方式消除它。 我的问题是我是否可以从调试
我有一个控制台程序,它链接到 Mac 上的 Foundation 框架。如何找到可执行文件所在的文件夹? 最佳答案 即使该工具不在 bundle 中,您仍然可以使用一些 NSBundle 方法。例如:
简单的问题是:如何找出 Cocoa 应用程序中可执行文件的位置。 请记住,在许多类 Unix 操作系统中,人们使用 PATH 环境来为其可执行文件分配首选位置,特别是当他们的系统中有同一应用程序的多个
如何找出 TGridPanel 内控件的位置(行和列索引)?我想对按钮数量使用常见的 OnClick 事件,并且需要知道按钮的 X、Y 位置。 我使用的是 Delphi 2007。 最佳答案 不幸的是
我试图找到一种方法来确定 .NET 应用程序中任意文件夹中的总磁盘空间和可用磁盘空间。文件夹中的“总磁盘空间”和“可用磁盘空间”是指如果您对其执行“dir”命令,该文件夹将报告的总磁盘空间和可用磁盘空
我希望能够通过 shell 脚本判断任何 POSIX 系统上是否存在命令。 在 Linux 上,我可以执行以下操作: if which ; then ...snip... fi 但是,Solar
如何找到不同 Haskell 函数的复杂性(以 big-O 表示)? 例如, subsequences 的复杂度是多少? ? 最佳答案 您只能通过查看代码来计算函数的确切复杂度。但是,您可以使用 cr
我试图找出我的对象占用了多少内存来查看有多少对象最终出现在 Large Object Heap 上。 (超过 85,000 字节)。 是否像为每个对象添加 4(表示 int)、添加 8(表示 long
一旦我在 Vim 中加载任何文件,它就会尝试检测该文件,并在可能的情况下用颜色突出显示它。 我想知道一个 Vim 命令,它会告诉我 Vim 认为哪个 ftplugin 或文件类型插件/文件类型会突出显
是否有可能找出 querySelector 的哪一部分与 DOM 中的特定元素匹配? 假设您有以下查询: 'h1,h2,h3,h4.custom-bg,div' 如果您使用 document.quer
我遇到一个问题,用户设置的区域设置(德语)与安装的语言 Windows(英语)不同。有没有办法发现安装的 Windows 语言与用户设置的区域设置?我应该注意的问题是我正在创建共享,并且根据区域设置设
我正在写入应用程序中的文件。我想找到该文件以检查该文件是否已正确写入(以便我可以通过 Web View 访问该文件)。这是我用来编写文件的代码: try { FileOutputStream
我有一个从 JSON 文件填充的 HashMap。键值对中的值可以是两种不同的类型 - 字符串或其他键值对。 例如: HashMap hashMap = new Map(); JSON 文件看起来有点
我是一名优秀的程序员,十分优秀!