注意:我考虑到大多数答案都在讨论它,因此我将kif添加到标题中只是为了搜索索引的目的。

我正在寻找iOS的Selenium之类的东西,基本上是一个可以在某个UI场景中运行很多次直到崩溃的测试自动化/单元测试框架,这将有助于我缩小很少发生的UI错误的原因。和随机。

(顺便说一句,我已经NSLogged了数据源/表交互的每一行代码,并花了数小时来分析潜在的原因..但是没有发现任何结论性..再次很少发生此错误)。

我看了一些unit testing frameworks in iOS,但它们似乎很多。我不确定该选哪个。我对 Selenium 的引用也是基于推测,因为我曾经与过去在大型Web项目中使用Selenium的质量检查人员合作过(我假设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感兴趣而不是我面临的特定问题的人:

经过一些研究和实验。.我将选择范围缩小到两个测试自动化库:
FrankKIF。我最终决定在借用cucumber's小 cucumber syntax来描述我的单元测试时使用KIF

我选择KIF(而不是Frank)的原因是KIF是100%基于obj-c的,而不是像Frank那样使用ruby。因此,设置更加简单,并且更适合我的狭窄测试用例需求。话虽如此,我承认如果我的应用程序更复杂(即使用来自多个服务器的输入等),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所做的巨大改进..现在您的测试感觉比过去更加自然和自然..(即,它实时进行..而不是创建 future 运行的场景等)。您甚至可以使用播放按钮等对每个按钮进行测试。您应该尝试一下。

以下是一些示例(再次使用小 cucumber 语法):
#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/

10-11 06:51