Unit Testing for a Complex Game I have known about unit testing for a while now, but I am just now finally understanding how to implement it.  I think that my initial implementation is a little rough so I could use some feedback. 

I have implemented these tests in order to verify that a new feature of my strategy game is working properly. Previously, I would have output these values to the screen somewhere and simply watched to make sure that the expected behavior was happening. With these tests, I have concrete proof that the feature is working.  This is much better!

However there are some problems. I needed to implement a Testing category for the class that I am testing because some of the methods I needed to run the tests are private. By creating this category at the top of the testing class, I can use private methods from the class without having to permanently expose them.  This works, but I have heard that I should only be unit testing public methods, so I am wondering if what I have done is acceptable. I should note that I also had to add a couple getter methods to the class to get some information that was not public.

DTDwarfTests.m

#import &lt;XCTest/XCTest.h&gt;
#import "DTDwarf.h"
#import "DTJobUnit.h"

@interface DTDwarf (Testing)
    -(void) setMoodForTesting:(int)moodAmount;
    -(void) startJobCountdown:(JobType)jobType;
    -(BOOL) doJobCountdown;
    -(void) calculateMoodState;
    -(void) calculateMoodAmountForJob;
    -(JobType) getFavoriteJob;
    -(JobType) getHatedJob;
@end

@interface DTDwarfTests : XCTestCase
@end

@implementation DTDwarfTests

- (void)setUp {
    [super setUp];
    // Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
    [super tearDown];
}

-(void) testMoodInitialization {
    DTDwarf *dwarf = [[DTDwarf alloc]initWithWorldSize:CGSizeZero];
    XCTAssert(dwarf.moodAmount &gt; 0);
}
-(void) testFavoriteAndHatedJobsValues {
    DTDwarf *dwarf = [[DTDwarf alloc]initWithWorldSize:CGSizeZero];
    XCTAssert([dwarf getFavoriteJob] != JobTypeNumJobTypes);
    XCTAssert([dwarf getHatedJob] != JobTypeNumJobTypes);
}
-(void) testFavoriteJobMoodInfluence {
    DTDwarf *dwarf = [[DTDwarf alloc]initWithWorldSize:CGSizeZero];
    [dwarf setMoodForTesting:0];
    dwarf.dwarfJobUnit = [[DTJobUnit alloc]initWithJobType:[dwarf getFavoriteJob]];
    [dwarf calculateMoodAmountForJob];
    XCTAssert(dwarf.moodAmount &gt; 0);
}
-(void) testHatedJobMoodInfluence {
    DTDwarf *dwarf = [[DTDwarf alloc]initWithWorldSize:CGSizeZero];
    [dwarf setMoodForTesting:1000];
    dwarf.dwarfJobUnit = [[DTJobUnit alloc]initWithJobType:[dwarf getHatedJob]];
    [dwarf calculateMoodAmountForJob];
    XCTAssert(dwarf.moodAmount &lt; 1000);
}
-(void) testBadMoodIncreasesCountdown {

    DTDwarf *happyDwarf = [[DTDwarf alloc]initWithWorldSize:CGSizeZero];
    [happyDwarf setMoodForTesting:1000];
    [happyDwarf calculateMoodState];
    [happyDwarf startJobCountdown:JobTypeMining];
    int happyDwarfCountdown = 0;
    while (![happyDwarf doJobCountdown]) {
        happyDwarfCountdown++;
    }

    DTDwarf *sadDwarf = [[DTDwarf alloc]initWithWorldSize:CGSizeZero];
    [sadDwarf setMoodForTesting:0];
    [sadDwarf calculateMoodState];
    [sadDwarf startJobCountdown:JobTypeMining];
    int sadDwarfCountdown = 0;
    while (![sadDwarf doJobCountdown]) {
        sadDwarfCountdown++;
    }

    XCTAssert(happyDwarfCountdown &lt; sadDwarfCountdown);
}

@end
Unit testing isn't something I do a whole lot of.  So as for a general review of what you've written, I can't attest much to that.

But as to the idea of creating a class category just to sorta-kinda exposed "private" methods for the sake of testing and testing only?  Well... let's talk about Objective-C, shall we?

Despite the way we talk about "methods", Objective-C doesn't really do... methods...  they're all messages which invoke underlying C functions.

In other languages, if you try to call a non-existent method, your code won't compile.  In Objective-C, you'll merely get a warning if the file you're in can't see the method you're trying to call, and if it really doesn't exist, you'll get a runtime crash.

But the fact remains that you can still compile the code and you can still pass the message to the object.  And if the object implements the method that matches the message, it will execute the code.

So even without the class category, we could still call the methods on the object--we'd just have a bunch of warnings.

And there's ways around the warnings as well.  This blog discusses this in more detail, but here's an example of doing this with one of your methods:

Given the method:

- (void)setMoodForTesting:(int)moodAmount;


We could invoke this method as such:

SEL mftSelector = NSSelectorForString(@"setMoodForTesting:");

((void (*) (id, SEL, float))objc_msgSend)(dwarf, mftSelector,1000);


It may also be worth looking into this blog entry for some information on cleaning this up and making it more reasonable to use.

In the end, this allows you to still test private details of the class, but these details aren't exposed anywhere to anyone.  If somehow you were to accidentally import this .m directly, you would then expose these private details and could accidentally use them somewhere.