In part 1, we discovered the basic usage of XCTestCase and wrote some simple unit tests for the Gun class using Swift. For a quick recap, this is what we got so far:

As you can see, each case in the method has its own unit test. They are arranged with this structure:

This is the traditional way of writing unit tests in which you list out all the test cases linearly. Each test case is separated and does not relate to each other.

Many projects still prefer this approach because it’s very simple and straightforward.

## But is that the best we can do?

Let’s look back at our example and further develop it. Currently we have 2 cases for the shoot method. (in pseudo code)

Let’s make it a little bit more complicated. Our Gun now can only shoot if it is unlocked. That would require some changes in the test cases like so:

And we need another 2 methods for locking/unlocking the Gun:

Now when we look at it, we see a line-by-line list with various conditions and scenarios. It’s easy to get lost.

You can’t tell what the Gun does just by taking a quick glance anymore. You have to stop and read each line carefully, which is not very ideal.

Wouldn’t it be great if we could group all the common parts among test cases?

Actually there is such way. It’s called Behavior-driven Development, or BDD for short.

The idea is: you describe how your class would behave from the top down to the bottom. You describe the big picture first, then go down to the nitty gritty.

Here is the BDD structure:

As you can see, there is no duplicated parts anymore. Tests are grouped by methods and cases. You can easily skim and still get the whole picture about what the class can do.

## Let’s try it with our Gun example

We will go from the very top (class) and down one level at a time.

First, we describe a Gun.

A Gun has 3 methods: shoot, lock and unlock.

The shoot method has 2 cases: has bullets and has no bullet.

The lock and unlock methods each only has one case.

In the has bullets case of the shoot method, we have 2 sub-cases: is unlocked and is locked.

And each case will have different behavior.

Again, from looking at this description, you quickly understand what a Gun can do in various cases. This saves you a lot of time and headache when reading tests.

## Translating into Swift

First, follow this guide (from step 1 to step 3) to setup cocoapods and add the Quick pod.

Add a new file called GunSpec.swift to your test target. Notice that unit tests written using BDD style should have the suffix Spec instead of Tests as the naming convention.

Your GunSpec class must subclass from QuickSpec and override the spec method.

Now we’re going to describe the Gun’s behaviors again. But this time, we do it with Swift.

First, we describe a Gun.

(Actually the line class GunSpec : QuickSpec already tells this. We don’t need to write any more code)

A Gun has 3 methods: shoot, lock and unlock.

The shoot method has 2 cases: has bullets and has no bullet.

The lock and unlock methods each only has one case. (Note that when translate to Swift code, we don’t use the word when. We use context instead)

In the context has bullets of the shoot method, we have 2 sub-cases: is unlocked and is locked

And each case will have different behavior.

At this point, we are done describing the Gun with all funtionalities. It’s time to write unit tests for each case. Let’s start with the first one.

We’re going to write the 3 steps for this test just like we did in the previous post, which are: setup, execution and expectation

Now when we run the test (⌘ + U), it will fail because we don’t have the property isLocked in the Gun class yet. Open your Gun.swift file and add this:

(If you forget where the Gun.swift is, please read part 1 again)

Run the test again. This time it should pass.

Let’s move on to the next case. This is the case where the Gun has bullets but it is locked and cannot shoot. Therefore, we expect the number of bullets to remain the same.

This test will fail. We haven’t implemented anything related to the isLocked logic in the Gun class yet. We will do it now:

Rerun the tests to make sure they all pass.

Now if we continue down the road with the remaining cases, we will have the full Spec for the Gun:

And the full Gun class with 2 additional methods: lock and unlock.

Although there is still some duplicated code in GunSpec.swift, you can refactor it out pretty easily using the beforeEach block as stated in Quick’s documentation.

The important thing is you know the flow. You know how to do things the BDD way.

## Wrap up

Today we discussed about the problem with traditional way of writing unit tests where we list things out linearly. We then introduced the BDD approach of designing tests from the top down so that it’s easier to read and navigate through cases.

Now I don’t say that you should dump the traditional way and only stick with BDD. If you’re just starting out, learn how to write a correct unit test first. Organizing your tests comes later.

The idea is that you get to know what options are available and which one to choose for your next project.

In the next article, I’ll be talking about Test-driven Development and how we can apply it to build a Calculator class from the ground up.

Stay tuned.