In this section, we're going to add a test suite for a new app: MovieTrivia You'll find the basic setup for this project in the Git repository. If you open the project, there are some view controllers, an Info.plist, and all the other files you would normally expect to exist in a regular project. There's also a JSON file in the project named TriviaQuestions.json. This file contains a couple of dummy questions that you can load by uncommenting a couple of lines in LoadTriviaViewController.swift.
By default, LoadTriviaViewController.swift attempts to load questions from a non-existing web server. This is intentional, to demonstrate how one would normally set up a project like this. Since we don't have a web server at our disposal right now, you can swap out the dummy networking code for the JSON file to test this app.
Before we write tests or perform any optimization, we should add a test target to the project. A test target is added similarly to how you added extensions before. You just select a different type of target: The iOS Unit Testing Bundle. The following screenshot illustrates the correct template you should select:
After adding the target, a new folder is added to your project. If you choose the default name for the test target, it's called MovieTriviaTests. In this target, we'll write all of our tests.
If you think about when we used files in multiple targets with extensions, you might expect that we're going to need to add all of the files we want to test to both of our targets. Fortunately, this isn't the case. When we write tests, we can import our entire app, enabling us to write tests for all of the code we have in the app target.
If you look inside the MovieTriviaTests folder, you'll find a single file; MovieTriviaTests.swift. This file contains a couple of hints about what tests should look like for your test suite. First of all, note that the test class inherits from XCTestCase. All of your test classes should inherit from this superclass to be discovered as a test.
First of all, you'll find a setUp() method in the test class. This method is used mainly for you to fulfill the first stage of the AAA pattern in testing: Arrange. You use this method to ensure that all of the preconditions for your test are met. You could make sure that your user is logged in or that your database is populated with test data. Note that the nature of this setup forces you to do something interesting. You can't write all of your tests in the same class because not every test has the same preconditions.
Separating tests into several classes is a good thing. You should create a test class for every unit of code that you're testing. One test class should typically not test more than a single class or struct in your app. If you're writing an integration test there might be more than one class involved in the test, but you should still make sure that you're only testing a single thing: The integration between the classes involved in the integration you're testing.
Also, note that there are two methods prefixed with test in the test class. These methods are executed as tests and they are expected to perform the act and assert steps. The majority of work should be performed in these test methods. Do note that it's often better to have multiple short test methods rather than a single test method that tests everything. The larger the methods, the harder it will be to maintain and debug your tests.
Finally, you'll find a tearDown() method. This method is intended to give you an opportunity to clean up after yourself. When you have inserted dummy data into your database, it's often desirable to remove this data when your tests are done. This will ensure a clean slate for the next test that runs and it minimizes the chances that your first test accidentally influences the second test that runs. As mentioned before, we don't want our tests to depend on other tests. This means that we also don't want to pollute other tests by leaving traces of previous tests.
Now that you have a test suite in place, let's see how we can write tests for our existing code and how it can be refactored to be properly tested.