Testing View Controller – Part 1

Now that you’ve tested the interactor and presenter in the Clean Swift VIP cycle. It’s time to finish up with the star of the show.

Drum roll please… View Controller

You’re going to finally take down the massive view controller. Massive leads to a massive post. So I’ll break this post into two parts:

In part 1, you’ll see the familiar setup and spy. You’ll then write tests for the expiration date picker to make sure a date is formatted as desired. We’ll diverge to look at the new invisible UI component and how it comes into play when testing view controllers. Finally, you’ll finish up by writing tests for the shipping method picker.

In part 2, you’ll write tests to make sure the keyboard behave as expected in all edge cases in which the text fields become first responder. You’ll also learn how to make running tests the same as running the app. Finally, you’ll test to make sure the pickers are configured properly.

Let’s get started.

Create the XCTestCase for the view controller

In Xcode, choose File -> New -> File, then select Test Case Class. Enter CreateOrderViewControllerTests for class. Make sure the CleanStoreTests target is checked. Click Create. The CreateOrderViewControllerTests.swift file is created.

The CreateOrderViewController’s input and output protocols are defined as follows:

Setup and Spy

With the interactor and presenter, setting up the subject under test is as simple as instantiating them, like CreateOrderInteractor() and CreateOrderPresenter(). In fact, the Clean Swift configurator creates them in the same way before hooking them up to the VIP cycle for real use. They are just POSOPlain Old Swift Object – with no parent class.

However, CreateOrderViewController inherits, either directly or indirectly, from UIViewController. It is loaded from the storyboard instead of created programmatically. This loading does a lot of things behind the scenes. Building the view hierarchy. Setting up the responder chain. Displaying the view on the screen. Among others.

Simply instantiating a view controller with CreateOrderViewController() doesn’t really fully set it up, ready to be used or tested.

But if you were to do all these things manually, you would need to dig deep into Apple’s documentation on view and view controller life cycles.

Fortunately, you don’t have to do that. You can just load it from the storyboard when you are running tests as if you are running the app.

You’ve seen similar code when you segue from one view controller to another. First, you get a bundle and a storyboard. And then call the instantiateViewControllerWithIdentifier() method with an identifier which you give to your view controller you create in the storyboard. It is almost always a good idea to set the identifier to the view controller class name.

But there are two very, very important things happening on the last line:

Continue reading →

Reader Question: If the business logic is inside of the Workers, then what’s the purpose of the interactor?

Marq asked a question on the role of the interactor and worker on my post about Testing Business Logic in Interactor.

I would be curious to see more about the worker concept as I am little confused by it’s purpose. The way that I interpreted it was more like a Service layer or Gateway (another boundary), which would retrieve objects (from a service, database, etc…) and then provide them to the interactor which would do something and pass the output back to the presenter. If the business logic is inside of the Workers, then what’s the purpose of the interactor? also where would code to a service or database then reside?

thanks,

That’s a good question. When you are adding your first worker to do some business logic, does it feel like you are just moving code from the interactor to the worker?

Does it then make the interactor unnecessary?

Why do you need an extra worker object to do stuff that your interactor already does?

Continue reading →

Testing Presentation Logic in Presenter

You learned how to test your business logic in the interactor using Clean Swift in my last post. Naturally, the flow of control leads us to the presenter today.

Specifically, you’ll be able to expand the role of a spy to make sure a method is invoked with the expected argument. You’ll also write a rather interesting test case to make sure a date is formatted correctly.

We’ll finish off by swapping out the spy with a mock. You’ll see how easy that is.

Create the XCTestCase for the presenter

In Xcode, choose File -> New -> File, then select Test Case Class. Enter CreateOrderPresenterTests for class. Make sure the CleanStoreTests target is checked. Click Create. The CreateOrderPresenterTests.swift file is created.

The CreateOrderPresenter’s input and output protocols are defined as follows:

Apply the Single Responsibility Principle (SRP) to Test Case

There is only one method to test. Yay! But look at the CreateOrderPresenter class’s presentExpirationDate() method. It seems to do quite a bit here.

As you recalled from the last post, formatting data is the presenter’s primary responsibility. The presentExpirationDate() method asks an NSDateFormatter object to return an NSDate in String representation.

The dateFormatter constant is set up outside of the method because we want to create it just once for efficiency reason. Later, we can reuse it for other formatting needs.

Even though it is not set up in the presentExpirationDate() method, you still want to test it in some way. But because it isn’t a boundary method, you don’t want to test it directly. Maybe NSDateFormatter is too slow, and you want to change the internal implementation in the future. So what do you do?

Continue reading →

Reader Question: Should you implement a business rule using the full VIP cycle or a property?

Darren asked me a question about my post on the Clean Swift iOS Architecture.

Clean Swift - VIP or Property Question

There are actually two parts to his question. So let me rephrase his question a bit:

  1. Sometimes your business logic is simply returning something from the underlying entity model layer. For example, fetching a list of available shipping methods from Core Data, over the network, or simply in a plist.

    Should you go through the whole VIP cycle and have empty method implementation that just pass the same piece of data through the interactor and presenter and back to the view controller?

    Or should you just return immediately from the interactor?

    Going through the whole VIP cycle seems pretty dumb and unnecessary. Returning immediately allows you to quickly move on to another feature without writing all these boilerplate protocols and methods.

  2. If you choose to return immediately, you can simply implement it as a property, or variable, or constant, instead of a method? It seems much simpler to do that. My example also implements shippingMethods as a property getter variable instead of a shippingMethods() method.

As you can probably imagine, I asked myself these two questions A LOT when I was designing Clean Swift and using it for all my projects. I encountered almost every possible situation.

Continue reading →

Testing Business Logic in Interactor

When it comes to writing tests for your app, how do you know which methods you need to test? I answered this question in my last post. Today, you’ll learn how to write tests for the CreateOrderInteractor class – the interactor that contains our CleanStore app’s business logic.

Let’s dive right in.

Create the XCTestCase for the interactor

In Xcode, choose File -> New -> File, then select Test Case Class. Enter CreateOrderInteractorTests for class. Make sure the CleanStoreTests target is checked. Click Create. The CreateOrderInteractorTests.swift file is created.

In the last post, you already learned that you need to test the boundary methods. Here are the CreateOrderInteractor’s input and output protocols again:

The nice thing about Clean Swift is it tells you exactly what methods you need to test at the top of the file. For CreateOrderInteractor, you need to write tests for both the shippingMethods variable and formatExpirationDate() method. You may ask – how do you test a variable? The shippingMethods variable is just a getter method, so it is no different, as you’ll soon see.

Continue reading →