TestsTested | ✓ |
LangLanguage | Obj-CObjective C |
License | MIT |
ReleasedLast Release | Dec 2014 |
Maintained by Unclaimed.
Foundry is a library for Objective-C developers that can take a lot of pain out of unit testing. One of the biggest problems during unit testing is the manufacturing of proper test/mock model objects. Imagine you have a situation where you have a "Person" object that you want to test. A person has properties like a name, email address, password, etc and so you would need to find a method for creating these test objects.
This is the simplest method where you just create objects and assign them static values like so.
Person *testPerson = [Person new];
testPerson.name = @"Randy Marsh";
testPerson.email = @"[email protected]";
testPerson.password = @"secret";
Well that's easy enough. Problem is it creates a very narrow path that doesn't really create great tests that are checking the durability of your models. Let's look at a few problems here:
So this method works for little stuff but is quickly unmanageable. What else is there?
This method sucks but we have all used it at some point or another. It goes something like this: first we define a utility method we can call to generate random strings of a given length, then we do stuff like this:
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);
This method allow us to create a "dynamic" object that can then be produced many times and get different values. But it still has a lot of problems:
We can do better right?
This is a good approach and it involves the use of a fake data library (such as Gizou which Foundry is built on) to populate the model properties. Because the data is assembled from a large prebundled set of valid names, prefixes, suffixes, etc it provides very real looking data. To use it you might do something like this.
Person *testPerson = [Person new];
testPerson.name = [GZNames name];
testPerson.email = [GZInternet email];
testPerson.password = [GZWords word];
Ok that's quite a bit better and we now have some nice dynamic objects with real looking data. But we still have a few disadvantages:
What we really need is something like factory_girl but for Objective-C...
Enter Foundry. It aims to solve all of these problems by giving you easy ways to mint model objects using "real" data with special handling for Core Data entities and runtime property setting. All we have to do to get started (after installing Foundry using Cocoapods and importing the "Foundry.h" header) is to take our Person model and adopt the TGFoundryObject protocol.
@interface Person : NSObject <TGFoundryObject>
...
Then just create a build spec for the object by implementing the single required method foundryBuildSpecs
. A build spec is a dictionary that tells Foundry how to build your model and looks like this:
+ (NSDictionary *)foundryBuildSpecs
{
return @{
@"name": [NSNumber numberWithInteger:FoundryPropertyTypeName],
@"email": [NSNumber numberWithInteger:FoundryPropertyTypeEmail],
@"password": [NSNumber numberWithInteger:FoundryPropertyTypeLoremIpsumShort]
};
}
Although the NSNumber wrappers are annoying this is still really simple. You just create the specs you want and then Foundry can now build your object like so.
// Let's build a single person first.
Person *fullPerson = [Person foundryBuild];
// Not enough, let's make 10 people!
NSArray *bunchOfPeople = [Person foundryBuildNumber:10];
Nice huh? What if you just want a hash of valid properties but not the full object?
// 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];
Well that's easy. But what if you use Core Data? Foundry has you covered.
// 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];
What if you want to manually assign the value yourself during the build process at runtime? Easy enough, you just set the type to FoundryPropertyTypeCustom
in the build spec and then you implement the protocol method.
(id)foundryAttributeForProperty:(NSString *)property
{
if ([property isEqualToString:@"name"]) {
// Add your custom assignment code here...
}
}
Right now you can set these using the custom property types but very soon Foundry will add the ability to nest factories so that you can assign a factory to a relationship attribute as part of your build spec. Stay tuned!
Foundry is built for use on both OSX 10.7 and above and iOS 6.0 and above. Your best bet to to install using Cocoapods.
If you want to run the test suite, clone the repo, run "pod install" in the main directory and then do "rake spec".
All classes are fully documented using appledoc, you can generate the docs from the repo using "rake docs:generate" or just get them from Cocoadocs.
John Tumminaro, [email protected]
Foundry is available under the MIT license. See the LICENSE file for more info.