Now that we have a functioning swift service which returns us name-spaced domain models, we can design a testing strategy for this class. First, we want to consider if mocking GraphQL fully is worth it to you. For example, could just you create a mock object which conforms to ApolloInterface
, and write your unit tests from there.
Instead, we are going to mock the Apollo client, and actually execute the Apollo queries with stubbed raw JSON. This is ideal because we actually test how our code interacts with Apollo. Additionally, you can use the JSON and the mocked NetworkTransport
object to simulate failed network or parsing states within unit testing, allowing us to gain more test coverage over the World.Store
networking object.
This testing strategy is borrowed from the Apollo-iOS test suite, which you can view the source for here .
First, let's look at the mocked NetworkTransport
object:
https://gist.github.com/875296283fcc0d7977bfe42dd9b95e1d
This class provides a mocked networking operation utilizing Dispatch
to simulate an asynchronous network process. You can also simulate a network failure.
Next, let's add some mocked components for Apollo. We will start with the cache.
https://gist.github.com/b43a77daf0f43fa8d2a16741386fd248
This class allows us to simulate the Apollo cache, and we can actually initialize this object with raw JSON to represent the cache state at the beginning of our unit test. This is useful for testing Apollo's different caching strategies. Here are the caching policies available to you with Apollo, and each caching strategies descriptions.
https://gist.github.com/58cffbe53fba642f8202d8aaa62b3b8e
We could potentially call our service methods with any of the below caching policies, and we may want to make sure that using .fetchIgnoringCacheData
actually returns from the network as apposed to the cache.
We won't go over having an initial cache state in this article.
Next, we will add the cache provider class. This object provides us with the ability to configure the mock cache object asynchronously.
https://gist.github.com/c50915f46a079f06ee3266eca9d2349c
In order to actually use this mocked ApolloClient
within a test, let's extend XCTestCase
for connivence. This will make our mocked client easily accessible within our unit tests.
https://gist.github.com/ee295c477925118bfa6a1f30688258ed
Now that we have our components to mock the ApolloClient
together, we need to create the resources which the server would return normally to Apollo. This data will consist of JSON, with a few added properties.
At its core, the GraphQL server just returns a JSON payload through POST
requests. In order to get this data, let's use the bash command curl
to send a post request to the GraphQL server to retrieve the raw JSON.
https://gist.github.com/d19d5f760e1dfe630ce628ef3e0c087d
This yields a JSON string which you can put through a formatter, or just pipe the result of this curl
command to jq
for json formatting.
https://gist.github.com/6d61fa28dddd80a327984431cfe62e42
Be sure to ensure you have added a __typename
key within the GraphQL query. If you forget to add the __typename
key, Apollo will fail to parse your JSON data.
From there, we add our server returned JSON payload into the mock GraphQL query enum
. The cases of this enum represent the queries we could be making, and this allows us to add more queries for testing in the future.
https://gist.github.com/13c7f588966c93524253359c3e151ad8
And now, with all of our required mocked classes and stubbed server JSON, let us write an actual unit test. The first test we write will test a successful network call and parse, this is the application's happy path.
https://gist.github.com/a062d1208d2e5aaf6db63bc08bfd0e58
Next, we will write a test to see what happens when we receive a .network
error from an Apollo network failure. This will simulate the phone loosing connection mid call, a network timeout, or another networking related error.
https://gist.github.com/c977e72afbd02e2a9ffcc6bfebd487f0
From there, we add a unit test to check the .parsing
error path. This could happen if GraphQL returns invalid JSON to the device. This path needs to be tested because our service code optionally unwraps GraphQL fragments into our domain model objects.
https://gist.github.com/b0a13efb103babffdee12869c983f579
That covers our CountriesQuery
GraphQL operation, next we need to write the same 3 tests for our CountryQuery
.
https://gist.github.com/e13a4e17841671aeec547306435bcbc4
π
That was a lot of tests. We now have 6 unit tests, covering our World.Store
object's methods and achieving 97.5% coverage. Not 98%, but close!