Foundry 0.1.1

Foundry 0.1.1

测试已测试
Lang语言 Obj-CObjective C
许可证 MIT
发布最新发布2014年12月

未申报维护。



Foundry 0.1.1

  • 作者:
  • John Tumminaro

介绍

什么是Foundry?

Foundry是一个面向Objective-C开发者的库,可以极大地减轻单元测试中的许多痛苦。在单元测试期间的最大问题之一是制造适当的测试/模拟模型对象。想象一下,您有一个想要测试的“Person”(人)对象。人有一些属性,如姓名、电子邮件地址、密码等,因此您需要找到一种创建这些测试对象的方法。

静态方法

这是最简单的方法,只需创建对象并分配静态值,如下所示。

Person *testPerson = [Person new];
testPerson.name = @"Randy Marsh";
testPerson.email = @"[email protected]";
testPerson.password = @"secret";

这很简单。问题是它创造了非常窄的道路,并不能真正创建检查您模型耐用性的好测试。让我们来看一下这里的一些问题:

  • 所有值都使用固定长度。所以当我们进行UI测试时,我们不会看到值超出我们提供的固定值会发生什么。
  • 如果我们需要创建多个对象,我们需要粘贴相同代码的不同值。我们可以使用相同的值,但我们不能测试唯一值。
  • 当我们更新属性时,需要大量维护来更改这些静态模拟。

所以这种方法对小事来说很好,但很快就无法管理。那还有别的吗?

使用随机字符串生成假数据

这种方法很糟糕,但我们都在某个时候使用过。它的过程是这样的:首先,我们定义一个可以调用以生成特定长度的随机字符串的实用方法,然后我们做这样的事情:

Person *testPerson = [Person new];
testPerson.name = randomStringWithCharacterCount(10);
testPerson.email = [NSString stringWithFormat@"%@@%@.com", randomStringWithCharacterCount(6), randomStringWithCharacterCount(6)];
testPerson.password = randomStringWithCharacterCount(arc4_random_uniform(8) + 6);

此方法允许我们创建一个“动态”对象,然后可以多次产生并得到不同的值。但它仍然存在许多问题:

  • 该方法产生了垃圾值,并不代表真实数据。随机字符字符串可能适合密码生成器,但不适合模型对象的属性。
  • 因为字符串是完全随机的,所以测试验证需要大量定制。
  • 它们通常是脆弱的,就像静态数据一样,并且随着模型对象的变化需要大量维护。
  • 这个方法通常让人筋疲力尽。

我们可以做得更好,对吧?

使用假数据库生成“真实”假数据

这是一个很好的方法,它涉及到使用一个伪造数据库(例如Gizou,它是Foundry的构建基础)来填充模型属性。由于数据是从一大组预打包的合法名称、前缀、后缀等中组装的,因此提供了非常真实的数据。要使用它,你可能需要这样做。

Person *testPerson = [Person new];
testPerson.name = [GZNames name];
testPerson.email = [GZInternet email];
testPerson.password = [GZWords word];

这样好多了,现在我们有一些不错的动态对象,拥有真实的数据。但仍有几个缺点

  • 这些工厂仍然需要对每个我们创建的类进行设置和维护,每次更改属性时更新起来都很痛苦。
  • 我们需要为每个类定义构造函数方法来构建对象,然后提供我们自己的循环来生成一系列它们。
  • 对于Core Data对象,我们还需要处理将对象插入托管对象上下文,然后执行保存操作。
  • 我们仍然无法在运行时自定义依赖于属性的属性进行特殊测试。设置这需要更多的工作。

我们真正需要的是类似于factory_girl的东西,但针对Objective-C...

使用Foundry

进来Foundry。它通过提供使用“真实”数据轻松铸造模型对象的方法来解决所有这些问题,并特别处理Core Data实体和运行时属性设定。我们开始使用Foundry(使用Cocoapods安装并导入“Foundry.h”头文件)所有要做的就是拿我们的Person模型并采用TGFoundryObject协议。

带有NSObject模型

@interface Person : NSObject <TGFoundryObject>
...

然后只需创建一个构建规范,通过实现单个必需方法foundryBuildSpecs来完成对对象的构建。构建规范是一个字典,告诉Foundry如何构建你的模型,如下所示

+ (NSDictionary *)foundryBuildSpecs
{
    return @{
        @"name": [NSNumber numberWithInteger:FoundryPropertyTypeName],
        @"email": [NSNumber numberWithInteger:FoundryPropertyTypeEmail],
        @"password": [NSNumber numberWithInteger:FoundryPropertyTypeLoremIpsumShort]
    };
}

尽管NSNumber包装器很烦人,但这仍然非常简单。你只需创建你想要的规范,然后Foundry就可以构建你的对象了。

    // Let's build a single person first.

    Person *fullPerson = [Person foundryBuild];

    // Not enough, let's make 10 people!

    NSArray *bunchOfPeople = [Person foundryBuildNumber:10];

很酷,对吧?如果你只想有一个有效属性的散列,而不是完整的对象呢?

    // Let's get a dictionary of attributes for a single person.

    NSDictionary *personDictionary = [Person foundryAttributes];

    // Now how about 10 people?

    NSArray *lotsOfPeople = [Person foundryAttributesNumber:10];

使用Core Data

这很简单。但是如果你使用Core Data怎么办?Foundry已经为你考虑到了。

    // Let's first build a person without saving them.

    Person *newPerson = [Person foundryBuildWithContext:context];

    // How about building AND saving a person?

    Person *anotherPerson = [Person foundryCreateWithContext:context];

    // How about creating 10 saved people?

    NSArray *tenPeople = [Person foundryCreateNumber:10 withContext:context];

自定义属性

如果你在构建过程中想运行时手动分配值怎么办?很简单,你只需在构建规范中将类型设置为FoundryPropertyTypeCustom,然后实现协议方法。

    (id)foundryAttributeForProperty:(NSString *)property
    {
        if ([property isEqualToString:@"name"]) {
            // Add your custom assignment code here...
        }
    }

关系

目前你可以通过自定义属性类型来设置这些内容,但不久Foundry将添加嵌套工厂的选项,使你可以将工厂分配给关系属性作为构建规范的一部分。敬请期待!

需求

Foundry是为OSX 10.7及更高版本和iOS 6.0及更高版本设计的。最好的办法是使用Cocoapods安装。

如果你想运行测试套件,请克隆回购库,在主目录中运行“pod install”,然后执行“rake spec”。

文档

所有类均使用appledoc完全文档化,您可以从存档中使用“rake docs:generate”生成文档,或者可以直接从Cocoadocs中获取它们。

安装

作者

John Tumminaro, [email protected]

许可证

Foundry 在 MIT 许可证下可用。更多信息请参阅 LICENSE 文件。