Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I unit test iOS view controllers?

How am I supposed to unit test my view controllers (UIViewController subclasses) in an iOS app?

I've been extensively googling this subject for the last couple of days but I still can't understand what is the proper way of doing this.

It seems that there are at least three ways of unit testing view controller logic:

  • Expose private actions and IBOutlets that you want to unit test (declare them in the header file).

  • Use a category, like Tests, to access private actions and IBOutlets from unit tests.

  • Don't expose anything, instead find buttons and other views by title or another public property and simulate user interaction via public UIView methods (e.g. simulate user tapping a button); then observe visible state.

    I didn't find a lot of sources on this one, but there is an example on objc.io.

Now, to be honest, I don't really like the first two because as far as I understand unit tests are not supposed to test object's internals (i.e. private methods), and declaring them public only for the sake of tests doesn't seem like the best practice. I usually keep IBActions and IBOutlets inside the implementation but now I suddenly have to make everything public only because I'm adding tests...

I think there might be another alternative: move as much logic as I can from my view controllers into independent components and test them instead (leaving controllers untested or mostly untested). This seems like a nice idea but I'll have to do lots of refactoring (I'm currently adding unit tests for a project that wasn't coded with testing in mind).

So I was wondering, what is the best way of testing view controllers?

like image 819
iosdude Avatar asked Oct 18 '22 22:10

iosdude


1 Answers

This is question may be primarily opinion-based and should be closed but I will post some of my opinions anyway.

IBAction and IBOutlet are not really private method/property. You may be able to declare them as private method/property but conceptually they are the public interface of you view controller. They are the way to communicate with views. Therefore, I will prefer the second way, use a category, like UI, to access private actions and IBOutlets from unit tests.

I totally against the third way for unit tests. Because by definition, you are writing integration test rather than unit test. It heavily rely on implementation details and can breaks easily. You may want a few of them but you should get unit tests done first.

The real solution, as you already aware, is to refactor the code to make them testable. Ideally, controller should be very small and only acts as the binding between view and models. If you have very large controller, you should refactor UI logic into view/view model and business logic in to model/model helper.

like image 131
Bryan Chen Avatar answered Nov 18 '22 13:11

Bryan Chen