Mikayel Aghasyan
Mikayel Aghasyan

Reputation: 225

How to make sure OCUnit test suite tearDown is called?

In our iPhone app unit tests we have one test suite which contains all test case classes. In the suite's setUp/tearDown we do general set up/tear down which creates/deletes some entities in DB. In setUp we use NSAsserts to asserts everything went right. The problem is that if something goes wrong in setUp, NSAssert causes crash and tearDown is not being called, leaving the DB uncleaned.

What is the best way to make sure tearDown is always being called so the DB is always clean? Maybe not to use NSAsserts? But then how to tell the testing framework to not run test cases?

Thanks.

Upvotes: 1

Views: 792

Answers (2)

Claus Broch
Claus Broch

Reputation: 9212

I'll suggest you add a boolean ivar to your test suite which is set in setUp when everything is setup correctly. The NSAssert is then replaced with setting this variable, eg. flagged by STAssert... in case anything goes wrong so it will cause your test to fail.

In each test case you then check that this ivar is true before performing the checks, e.g. by using something like this:

-(void)setUp {
  // Perform the setup of the testbed and setting testBedStable accordingly
  STAssertTrue(testBedStable, @"Failed to setup test environment";
}

-(void)testSomething {
  if(testBedStable) {
    // Perform tests
  }
  else
    STFail(@"Unable to perform test case");
}

This method will ensure tearDown is always called and you can clean up accordingly.

Upvotes: 2

Jon Reid
Jon Reid

Reputation: 20980

Right, don't use NSAssert. Instead:

  • Pull database setup into separate helper methods.
  • Set instance variables to indicate what was successfully set up.
  • STFail on anything that isn't successfully set up.
  • Have each test call the appropriate helper methods.
  • In -tearDown, check the instance variables to see what needs to be cleaned up.

Example:

@interface DatabaseTest : SenTestCase
{
    BOOL removeTestDataInTearDown;
}

- (void)addTestDataToDatabase
{
    BOOL success;
    // Attempt to add data to database. Set success according to results.
    if (!success)
        STFail(@"Unable to add test data to database", nil);
    removeTestDataInTearDown = YES;
}

- (void)removeTestDataFromDatabase
{
    // Remove data from database.
}

- (void)tearDown
{
    if (removeTestDataInTearDown)
        [self removeTestDataFromDatabase];

    [super tearDown];
}

- (void)testSomething
{
    [self addTestDataToDatabase];
    // Execute test using data.
}

Note that even this has the potential to leave cruft in the database, which makes such tests fragile. So you might complement such tests with a different set of tests that uses mock objects in place of real database calls.

Upvotes: 1

Related Questions