FactoryBird is an objective-c library to easily create a set of fake models fixture for unit testing. It allows to easily configure and create objects with fake data that you can use in your tests to have an isolated know state.
That's because objective-c's FactoryBird is built with ruby's factory_girl capabilities in mind.
It is mostly inspired by Foundry but it has some additional flexibility:
- You can call
build
to build a default test object or callbuildUsing:
passing a dictionary with some properties that will override the default ones. - You can build an array of objects and also override some properties for all objects or just for some, all in one single method.
- It is decoupled from the faker library/logic. You can use whatever faker pod you like when implementing
FactoryBirdObject
protocol. I'm using Gizou. - Works with subclasses' inherited properties; ie. for a given object, you can set a property of any of it's superclasses.
- Allows subclassing both NSObject and NSManagedObject factories to have several handy configurations for the same object. No matter that the entity is not in the xcdatamodel when using Core Data 🎉.
♥ CocoaPods.
pod 'FactoryBird', '~> 0.0.1'
-
Add FactoryBird to your tests targets, all of them where you want to use it. For eg. Unit and Snapshot tests.
-
CLEAN TIP: create a separate
Factories/
folder at the same level of your tests' targets folders that will hold objects' factories:
Tests/
Factories/
UnitTests/
SnapshotTests/
If using Core Data, it's nice to have NSManagedObjects separated from regular NSObjects:
Tests/
Factories/
CoreData/
Ponsos/
UnitTests/
SnapshotTests/
Defining a factory for an object is just making its class conform to the <FactoryBirdObject>
protocol.
#import "FactoryBird.h"
@interface Task <FactoryBirdObject>
There is only one method to implement:
+ (NSDictionary *)factoryAttributes
Here you create the dictionary with the key-value pairs of attributes you want for the fake object.
+ (NSDictionary *)factoryAttributes
{
return @{ @"title" : @"Title for testing",
@"dueOn" : [NSDate dateWithTimeIntervalSince1970:1425314661],
@"resolved" : @NO,
@"taskID" : @1,
@"description": @"Lorem ipsum dolor est"
};
}
Keys can be any property
of the class or any of its superclasses. In the case of NSManagedObjects, both attributes
and relationships
(since they are properties
).
When you need a factory for a model, you can make the model class conform to the <FactoryBirdObject>
protocol directly.
However, you can also leave the class unchanged and add a new category to the tests target/s, inside the Factories/
folder. This way, you keep your model code clean and the factory methods will only be available inside the tests' target where they really belong 😉.
Tests/
Factories/
Task+FactoryBird.h
Task+FactoryBird.m
UnitTests/
SnapshotTests/
Task+FactoryBird.h
header file:
#import "FactoryBird.h"
@interface Task (FactoryBird) <FactoryBirdObject>
@end
Once you've defined the factory, creating fresh fixtures in your tests is very simple.
Task *task = [Task build];
Task *resolvedTask = [Task buildUsing:@{@"resolved":@YES}];
The dictionary you pass in will add the properties to the Task
object if the factory doesn't define them or override them if it does.
NSArray *tasks = [Task buildTimes:20];
NSArray *tasksWithProjects = [Task buildTimes:20 using:^NSDictionary *(NSUInteger idx) {
return @{@"project": [Project build]};
}];
User *user = [User buildWithContext:context];
User *maria = [User buildWithContext:context using:@{@"name":@"Maria"}];