Docs
Launch GraphOS Studio

Test mocks


When unit testing code that uses your generated models, you will often want to create response models with mock data. provides generated test mocks that facilitate this.

Purpose

Generated test mocks provide a type-safe way to mock your response models. Rather than dealing with cumbersome and error prone JSON data, test mocks make mocking safer, more concise, and reusable. They are mutable, offer code completion in Xcode, and update with new automatically as your schema and change.

Mocking GraphQL responses with JSON

Because the generated response models are backed by a JSON dictionary, initializing them with mock data without generated test mocks is verbose and error-prone. You need to create stringly-typed JSON dictionaries that are structured exactly like the expected network response to ensure that your models parse properly.

let data: [String: Any] = [
"data": [
"__typename": "Query",
"hero": [
"__typename": "Human",
"id": "123",
"name": "Obi-wan Kenobi",
"friends": [
[
"__typename": "Human",
"id": "456",
"name": "Padme Amidala"
],
[
"__typename": "Droid",
"id": "789",
"name": "C-3PO"
]
]
]
]
]
let model = try HeroAndFriendsQuery.Data(data: data)
XCTAssertEqual(model.hero.friends[1].name, "C-3PO")

Constructing and maintaining these JSON dictionaries in your tests is cumbersome and time consuming.

Mocking GraphQL responses with generated test mocks

Generated test mocks provide a type-safe way to build mocks of your response models, without using stringly-typed JSON.

let mock = Mock<Query>(
hero: Mock<Human>(
id: "123",
name: "Obi-wan Kenobi",
friends: [
Mock<Human>(
id: "456",
name: "Padme Amidala"
),
Mock<Droid>(
id: "780",
name: "C-3PO"
)
]
)
)
let model = HeroAndFriendsQuery.Data.from(mock)
XCTAssertEqual(model.hero.friends[1].name, "C-3PO")

Setting up test mocks

To generate test mocks along side your schema types and operation models, configure your code generation configuration to include them as described in code generation configuration - Test mocks.

Once you've generated test mocks and linked them to your unit test target, you will need to import ApolloTestSupport in your unit tests to start using them.

import MyTestMocksTarget
import ApolloTestSupport
import XCTest
class HeroAndFriendsQueryFetchingTests: XCTestCase {
...
}

Usage

in a operation can be of abstract types (interface or union). This means we don't always know what type of object a in a response model will be. When using type conditions in our operations, the possible values for a response object can be very different depending on what is returned for the field. Because of this, generating intializers for our operation response models directly would require tens or hundreds of different generated initializers for every response object.

Instead, Apollo iOS generates MockObject types based on your schema . You create a mock of a concrete object type from your schema as, and set fields on it. Then you can use this mock object to initialize any response model. This allows you to build mock data for objects and use them with multiple tests and response models.

Creating test mocks

You can create a mock of any object type by initializing the generic Mock<MockObject> class and specifying a MockObject type.

Because test mocks require you to use a concrete object type, the __typename field automatically included whenever the object is used in a response model.

let grevious = Mock<Droid>()
print(grevious.__typename) // "Droid"

Setting properties on test mocks

Each MockObject has generated properties for each field that could be fetched on that object type across all the operations in your project. If a field on your is never fetched by your application, it won't be generated on your test mock objects.

When using your test mocks in a unit test, you may only be concerned about a subset of the required fields on the response object. Test mocks don't require you to initialize values for any fields you are not using in your tests.

You can initialize them with no data, and then set only the fields relevant for your test. Apollo iOS also generates convenience initializers for each concrete object type.

let grevious = Mock<Droid>()
grevious.name = "General Grevious"
grevious.numberOfArms = 4
let dooku = Mock<Human>()
dooku.name = "Count Dooku"
grevious.friends = [dooku]
let grevious = Mock<Droid>(
name: "General Grevious",
numberOfArms: 4,
friends: [
Mock<Human>(name: "Count Dooku")
]
)

Creating response models from test mocks

Once you've initialized your mock objects, you can construct any of your generated response models with them. You do not need to create mocks for an entire operation's response model, just the objects your are using in your tests. You can also convert your mocks into your generated fragment models.

All generated response models conform to the SelectionSet protocol. To create a model with a test mock, call from(_ mock:) on the model's type.

let mockLuke = Mock<Human>(name: "Luke Skywalker")
let heroQueryModel = HeroAndFriendsQuery.Data.Hero.from(mockLuke)
let mockLeia = Mock<Human>(name: "Leia Organa")
let heroDetailsFragmentModel = HeroDetails.from(mockLeia)

You can then use the created response models in your unit tests.

Note: Make sure to set all of the properties on your mocks that are going to be accessed during your test's execution. If your program accesses a required property on a response model and it's test mock does not contain that field, your tests will crash!

Reusing test mocks

The same test mock can be used to create multiple different response models.

let mock = Mock<Human>(name: "Luke Skywalker")
let hero = HeroAndFriendsQuery.Data.Hero.from(mock)
let heroDetails = HeroDetailsFragment.from(mock)
let mutationResponseModel = HeroDetailsMutation.Data.Hero(mock)

While Mock<MockObject> a class, your generated response models are structs with value semantics. This means that when you create a response model, it creates it's own copy of the test mock's data. If you mutate the test mock's data after creating a response model, that model's data will be unaffected.

let mock = Mock<Human>(name: "Luke Skywalker")
let hero = HeroAndFriendsQuery.Data.Hero.from(mock)
print(hero.name) // "Luke Skywalker"
mock.name = "Leia Organa"
let heroDetails = HeroDetailsFragment.from(mock)
print(heroDetails.name) // "Leia Organa"
/// The `hero` has not had it's data mutated.
print(hero.name) // "Luke Skywalker"
Previous
Multi-query Pagination
Next
Including Apollo as an XCFramework
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company