使用 Cedar 测试presentedViewController 的存在

Posted

技术标签:

【中文标题】使用 Cedar 测试presentedViewController 的存在【英文标题】:Testing the existence of a presentedViewController using Cedar 【发布时间】:2014-10-14 16:32:35 【问题描述】:

当我自动点击表格行单元格时,我正在尝试测试是否存在呈现的视图控制器。当我尝试测试控制器的presentedViewController 是否属于给定类型的类时,结果总是为零。我假设新呈现的视图控制器正在转换为 作为呈现的视图控制器,这就是 [controller presentViewController] 为 nil 的原因。

我正在使用 Cedar BDD 测试框架。我已经安装了 PivotalCore 库来提供自动“点击”功能。

这是规范代码:

#import <Cedar-ios/Cedar-iOS.h>
#import "UITableViewCell+Spec.h"

#import "FMNavigatorViewController.h"

using namespace Cedar::Matchers;
using namespace Cedar::Doubles;

SPEC_BEGIN(FMNavigatorViewControllerSpec)

describe(@"FMNavigatorViewController", ^
    __block UINavigationController *nav;
    __block FMNavigatorViewController *controller;

    beforeEach(^
        FSHandle *documents = [FSHandle handleAtUrl:[[BasicFileManager sharedManager] documentsUrl] isDirectory:YES];
        // @todo Remove all files from Recent Files and Local Files.
        // Remove all configured remote connections.
        NSArray *contents = [[BasicFileManager sharedManager] contentsOfDirectoryAtURL:documents.url];
        for (NSURL *url in contents) 
            if (! [url.lastPathComponent isEqualToString:@"Local Files"] && ! [url.lastPathComponent isEqualToString:@"Recent Files"]) 
                NSLog(@"WARNING: Deleting Manager: %@", url.lastPathComponent);
                FileManager *manager = [FileManager fileManagerWithName:url.lastPathComponent];
                [manager deleteFileManager];
            
        
        // Create view.
        controller = [[FMNavigatorViewController alloc] initWithDirectory:documents];
        nav = [[UINavigationController alloc] initWithRootViewController:controller];
        // Initiates view lifecycle. Accessing the 'view' will automatically
        // create it.
        nav.view should_not be_nil;
        // Doesn't get called unless properly added to a heirarchy -- which I
        // haven't found the correct process for yet.
        [controller viewWillAppear:NO];
    );

    it(@"should contain Local and Recent Files with no other connections", ^
        controller should be_instance_of([FMNavigatorViewController class]);
        // Local and Remote Connection Groups
        [controller.tableView.dataSource numberOfSectionsInTableView:controller.tableView] should equal(2);
        // Recent Files and Local Files
        [controller.tableView.dataSource tableView:controller.tableView numberOfRowsInSection:0] should equal(2);
        // Enforce order: Local Files then Recent Files.
        [[[controller.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] textLabel] text] should equal(@"Local Files");
        [[[controller.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]] textLabel] text] should equal(@"Recent Files");
        // The second group should have one row with description.
        [controller.tableView.dataSource tableView:controller.tableView numberOfRowsInSection:1] should equal(1);
        [[[controller.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]] textLabel] text] should equal(NSLocalizedString(@"CreateRemoteConnection", @""));
    );

    it(@"should display the FM wizard view", ^
        [[controller.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]] tap];
        controller.presentedViewController should_not be_nil;
        //[nav presentedViewController] should be_instance_of([UINavigationController class]);
        //[controller presentedViewController] should be_instance_of([UINavigationController class]);
    );

);

SPEC_END

最后的测试包含有问题的代码。我的问题是:如果presentViewController 不为零,我是否需要等待一两秒才能测试?如果是这样,我该怎么做?

这是点击单元格后应执行的代码:

FMWizardViewController *controller = [FMWizardViewController new];
[controller setDelegate:self];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:controller];
nav.navigationBar.tintColor = UIColorFromRGB(BNAV_TINT_COLOR);
[self presentViewController:nav animated:YES completion:nil];

我仔细检查以确保在点击单元格后该代码确实会运行;确实如此。

谢谢!

【问题讨论】:

【参考方案1】:

在 Google cedar-discuss 小组 (https://groups.google.com/forum/#!forum/cedar-discuss) 的帮助下,我得以解决。

需要做的是:

    需要改进主运行循环,以便实例化presentViewController 并将其关联到相应的视图控制器 需要创建整个视图层次结构(窗口、导航控制器、FMNavigationController)

#import &lt;Cedar-iOS/Cedar-iOS.h&gt;

#import "UITableViewCell+Spec.h"

#import "FMNavigatorViewController.h"

// << -- Add this
#define tickRunLoop (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.01, false))

using namespace Cedar::Matchers;
using namespace Cedar::Doubles;

SPEC_BEGIN(FMNavigatorViewControllerSpec)

describe(@"FMNavigatorViewController", ^
    __block UIWindow *window; // <<-- and this,
    __block UINavigationController *nav;
    __block FMNavigatorViewController *controller;

    beforeEach(^
        window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        FSHandle *documents = [FSHandle handleAtUrl:[[BasicFileManager sharedManager] documentsUrl] isDirectory:YES];
        // @todo Remove all files from Recent Files and Local Files.
        // Remove all configured remote connections.
        NSArray *contents = [[BasicFileManager sharedManager] contentsOfDirectoryAtURL:documents.url];
        for (NSURL *url in contents) 
            if (! [url.lastPathComponent isEqualToString:@"Local Files"] && ! [url.lastPathComponent isEqualToString:@"Recent Files"]) 
                NSLog(@"WARNING: Deleting Manager: %@", url.lastPathComponent);
                FileManager *manager = [FileManager fileManagerWithName:url.lastPathComponent];
                [manager deleteFileManager];
            
        
        // Create view.
        controller = [[FMNavigatorViewController alloc] initWithDirectory:documents];
        nav = [[UINavigationController alloc] initWithRootViewController:controller];
        window.rootViewController = nav;
        [window makeKeyAndVisible];
        // Initiates view lifecycle. Accessing the 'view' will automatically
        // create it.
        nav.view should_not be_nil;
        // Doesn't get called unless properly added to a heirarchy -- which I
        // haven't found the correct process for yet.
        [controller viewWillAppear:NO];
    );

    it(@"should contain Local and Recent Files with no other connections", ^
        controller should be_instance_of([FMNavigatorViewController class]);
        // Local and Remote Connection Groups
        [controller.tableView.dataSource numberOfSectionsInTableView:controller.tableView] should equal(2);
        // Recent Files and Local Files
        [controller.tableView.dataSource tableView:controller.tableView numberOfRowsInSection:0] should equal(2);
        // Enforce order: Local Files then Recent Files.
        [[[controller.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] textLabel] text] should equal(@"Local Files");
        [[[controller.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]] textLabel] text] should equal(@"Recent Files");
        // The second group should have one row with description.
        [controller.tableView.dataSource tableView:controller.tableView numberOfRowsInSection:1] should equal(1);
        [[[controller.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]] textLabel] text] should equal(NSLocalizedString(@"CreateRemoteConnection", @""));
    );

    it(@"should display the FM wizard view", ^
        // Sanity to ensure we are tapping the right cell.
        [[controller.tableView.visibleCells[2] textLabel] text] should equal(NSLocalizedString(@"CreateRemoteConnection", @""));
        [controller.tableView.visibleCells[2] tap];
        tickRunLoop; // <<-- and this.
        controller.presentedViewController should_not be_nil;
        controller.presentedViewController should be_instance_of([UINavigationController class]);
    );

);

SPEC_END

现在所有测试都通过了。

其中一位维护人员表示,该问题很可能与 iOS 8 中的一些内部更改有关;一旦调用 presentViewController:animated:completion: ,它就不会关联 presentViewController 值。应该在以后解决。

我希望这对某人有帮助!

更新

我忘了补充一点,推进运行循环并不是最佳实践。在问题得到解决之前,这应该被视为权宜之计。

【讨论】:

以上是关于使用 Cedar 测试presentedViewController 的存在的主要内容,如果未能解决你的问题,请参考以下文章

如何设置 cedar 以对 ios 应用程序进行 bdd 测试?

iOS测试控制器与Cedar

Heroku cedar:响应时间比竹子慢?

Cedar - 检查是不是使用不同的值调用了两次方法

如何在 Heroku Cedar 堆栈上使用 .htaccess 重定向到 HTTPS

Heroku 推送被拒绝,未检测到 Cedar 支持的应用程序