Getting Started With Behaviour-Driven Development

I’ve written before on the subject of Behaviour-Driven Development, here and here. I have found it invaluable in answering the question “what should I test?” Focussing on behaviours rather than tests, helped me a great deal. A focus on validating behaviours soon becomes second nature and I found it a lot easier than Test-Driven Development.

Objc.io puts it nicely

Let’s consider an object that is a part of an app you wrote. It has an interface that defines its methods and dependencies. These methods and these dependencies declare contract of your object. They define how it should interact with the rest of your application and what capabilities and functionalities it has. They define its behavior.
And that is what you should be aiming at: testing how your object behaves.

In my first attempts at Behaviour-Driven Development in iOS I used Specta, I love Specta but it had some problems.

  • It is native to Xcode so whilst it kind of works some things are not great. Like it is difficult to run just one test.
  • It can be difficult to know which test failed.
  • It can be kinda buggy

Test Naming

I used obj.io article Real-World Testing with XCTest for guidance, I knew I wanted Behaviour-Driven Tests in XCTest. I really liked the style of Specta tests and being a Ruby Developer they were familiar to RSpec. One piece of advice the article gives is to start all tests with the words “testThatIt”. This small change although it may feel insignificant helps to structure the naming of test and keeps you focussed on being Behaviour-Driven. So I took an existing Specta test class for User Management and converted the wording of the tests to “testThatIt”

Specta XCTest
should be logged in successfully testThatItLogsUserInSuccessfully
should return error when service returns HTTP status code 401 testThatItReturnsErrorWhenServiceReturns401
should not accept blank email or password testThatItDoesNotAcceptBlankEmailOrPassword

Split Bigger Test Suites Into Topics

The Real-World Testing with XCTest article had a little snippet of advice in there “if a class gets a bit bigger, we use categories to split up the test by topics.” This advice appealed to me because it is similar to the way that Specta works with contexts and describe blocks. Splitting up test classes by topics wasn’t as easy as I initially thought it would be but didn’t prove too difficult.

DDUserTests.m

@interface DDUserTests: XCTest {
@end
    
@implementation DDUserTests {
  - (void)setUp {
    [super setUp];
  }

  - (void)tearDown {
    [super tearDown];
  }
@end


DDUserTests+Notifications.m

@implementation DDUserTests (Notifications) {
  - (void)testThatItPostsANotificationWhenUserLogsIn { 
    // ARRANGE
    ...

    // ACT
    ...

    // ASSERT
    ...
  }
@end

Test Structure

I have found it useful to structure the actual tests using an Arrange-Act-Assert pattern, splitting each test into three parts. The Arrange section allows you to set-up the environment, so this may be mocking out network calls or putting in place stub objects. Then the Act section is the thing you want to test, this will typically be one method call. The Assert section validates you got the expected outcome.

Again taking inspiration from objc.io

 - (void)testThatItDoesURLEncoding
    {
        // ARRANGE
        NSString *searchQuery = @"$&?@";
        HTTPRequest *request = [HTTPRequest requestWithURL:@"/search?q=%@", searchQuery];

        // ACT
       NSString *encodedURL = request.URL;

       // ASSERT
       XCTAssertEqualObjects(encodedURL, @"/search?q=%24%26%3F%40");
    }

I am learning lots at the moment about testing in iOS, it is a different world to what I am used to in Ruby and .NET. As I learn more I will be sharing the findings in more blog posts.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *