3

我想对我的 Core Data 应用程序进行单元测试(带有许多记录的压力测试)。一切都是为单元和应用程序测试而设置的,并且工作正常。

我想创建许多核心数据对象,然后看看我的图形视图控制器是否仍然有效。我该怎么做?

如果我在我的MyAppApplicationTest.m测试类中创建一个测试方法,测试将在测试后终止应用程序,我无法与图形视图控制器进行交互。

我是否坚持必须在我的代码中创建许多记录AppDelegate并稍后删除该代码?或者有没有办法使用单元测试框架?

谢谢你的帮助。

4

1 回答 1

1

UI 测试有多种选择。但是,在这种情况下,我建议建立一个巨大的数据库,并保留它以进行各种测试。您可以选择通过在命令行、环境中或仅在用户默认值中设置值来使用它。

来示例代码来检查用户默认值,然后是环境设置...

static NSString * findOption(NSString *name) {
    NSString *result = nil;
    NSDictionary *options  = [NSUserDefaults standardUserDefaults];
    if ((result = [options objectForKey:name]) != nil) return result;
    options = [[NSProcessInfo processInfo] environment];
    if ((result = [options objectForKey:name]) != nil) return result;
    return nil;
}

注意,如果你只想检查命令行参数,而不是用户默认的所有域,你可以使用这个...

NSDictionary *options  = [[NSUserDefaults standardUserDefaults] volatileDomainForName:NSArgumentDomain];

然后,在创建持久存储的代码中,您可以查看是否设置了该选项...

if ((value = findOption(@"MundiLargeData")) && value.boolValue) {
    // Create the persistent store with the pre-generated big database
    // If creation failed, can continue with normal database as failsafe
}

另外,请注意,如果您使用 SenTest 进行测试,它使用命令行参数:

NSString *value = findOption(@"SenTest");
if (value) {
    NSLog(@"Using SenTest: %@", value);
}

您可以保留代码,或#ifdef 将其保留。离开那里很安全。

编辑

道歉 - 我打算立即添加这个,但被叫走了......

对于那个很抱歉。我从来没有暗示你会发布你的测试代码。你当然不想那样做。我以为您只是在寻找一种在运行应用程序时加载大型数据库的方法,这样您就可以在设备上进行手动 UI 测试,而无需编译不同的版本。

如果你想做这样的事情,那么你有很多选择。您可以将测试编写为要测试的类的类别,然后将该文件从发布版本中排除。如果你给你的测试一个一致的命名方案,比如前缀为“test”或“runtimeTest”,那么你可以有一个这样的方法......

- (void)runAllMethodsThatBeginWith:(NSString*)prefix {
    Class aClass = [self class];
    Method *methods;
    unsigned methodCount;
    if ((methods = class_copyMethodList(aClass, &methodCount)))
    {
        // For this example, we only want methods that take no arguments and return void
        char const *desiredEncoding = method_getTypeEncoding(class_getClassMethod([NSObject class], @selector(load)));

        for (unsigned i = 0; i < methodCount; ++i) {
            SEL selector = method_getName(methods[i]);
            NSString *name = NSStringFromSelector(selector);
            char const * typeEncoding = method_getTypeEncoding(methods[i]);
            NSLog(@"%@: %s %s", name, typeEncoding, desiredEncoding);
            NSRange range = [name rangeOfString:prefix];
            if (range.location == 0 && range.length == prefix.length && strcmp(desiredEncoding, typeEncoding) == 0) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                [self performSelector:selector];
#pragma clang diagnostic pop
            }
        }

        // Don't forget to free the allocated methods array
        free(methods);
    }
}

它会在你的类中找到所有以某个名称开头并返回 void 且不带参数的方法。您可以进行其他参数处理,但随后您将不得不处理与 ARC 相关的问题(因为编译器不确定该做什么——它至少会给您一个警告)。无论如何,这只是为了让您入门...您可以添加类型编码作为参数,并使其更通用...

现在,在您的运行时代码中,您只需调用...

[self runAllMethodsThatBeginWith:@"runtimeTest"];

它将运行所有看起来像...的方法

- (void)runtimeTestFoo {
}

如果没有,那么它只会默默地什么都不做。

您可以从发布版本中排除具有这些实现的整个文件,或者仅使用宏 ifdef 排除它们。

现在您的任何测试都没有编译到版本中,但它们用于其他东西,您可以随时调用您的测试。如果您知道特定的测试,那么您当然可以只使用 respondsToSelector: 并有条件地运行该特定的测试方法。

编辑

嗯。我以为您正在寻找某种方法来动态决定要做什么。如果这就是你想要的,那么只需提供一个创建数据库的 AppDelegate 的子类......

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Create your mondo database
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

现在,您有多种选择。在 main.o 中,您告诉它要使用哪个应用程序委托类。您可以使用选项(#ifdef DEBUG)、环境变量或其他方式来告诉它使用哪个应用程序委托类...

#import "AppDelegate.h"
#define APP_DELEGATE AppDelegate

#ifdef USE_MY_SPECIAL_RUNTIME_TEST_DELEGATE
#import "RuntimeTestDelegate.h"
#undef APP_DELEGATE
#define APP_DELEGATE RuntimeTestDelegate
#endif

int main(int argc, char *argv[])
{
    @autoreleasepool
    {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([APP_DELEGATE class]));
    }
}

或者,它可以只调用 NSClassFromString(@"MyTestingAppDelegate") 来查看它是否被链接到调用它......

或者,如果您想要完全分离,只需创建另一个目标。在那里有应用程序委托子类,并在 main.m 中为该目标使用它。链接到所有其他文件。

现在您有了一个完全独立的可执行文件,它与“生产”相同,只是它有一个特殊的应用程序委托,可以在启动应用程序之前构建数据库。

测试很难。你必须确切地知道你想要什么和不想要什么。没有涵盖所有情况的正确答案。

还有许多其他选项,例如在资源包中提供配置文件,包括应用程序 plist 中的额外内容,为应用程序提供“大师”模式,您可以在执行期间向其发送特殊命令(比如拥有它打开一个套接字并读取特殊命令并发送回响应——这样你就可以为你想要的任何场景编写脚本,在你的 Mac 上运行它们并远程控制应用程序——也有用于此的工具)。

希望其中一种方法适合您的需求。

于 2012-08-02T21:13:00.970 回答