#
Fixtures
Poser generates random data, meaning fixtures are not required by default. However, for some use-cases, fixtures can be useful.
#
Adding fixtures
Fixtures can be provided through the graphql.fixtures
key in the ServiceMock
object:
const myService = {
graphql: {
typeDefs: `
type Query {
books: [Book]
}
type Book {
id: Int
title: String
reviewCount: Int
}
`,
fixtures: {
Book: [
{ id: 1, title: 'The amazing story' },
{ id: 2, title: 'A great story' },
{ id: 3, title: 'Greatest letters assembled into words' }
]
}
}
}
When adding fixtures, it pre-populates the store, very much like fixture records would be inserted into a database for testing purposes.
Querying the store for an id specified by a fixture will not only return the fixture data, but the fields that were not defined in fixtures will also be auto-mocked!
store.get('Book', 2, 'title'); // "A great story"
store.get('Book', 2, 'reviewCount'); // a random auto-mocked integer
The store is still able to auto-mock values for any other Book
that is not defined by fixtures:
store.get('Book', 45, 'title'); // a random string
#
Fixtures and queries
By default, queries do not return fixtures. To return fixtures in the books
query, a books
resolver must be specified in one of two ways:
#
1. Return fixtures in the resolver
Specify a "fixture aware" resolver:
import { utils } from '@datacamp/poser';
const fixtures = {
Book: [...] // same as above
}
const myService = {
graphql: {
typeDefs: '...', // same as above
fixtures, // we moved fixtures in a variable
resolvers: {
Query: {
books: () => {
return fixtures.Book.map(book => utils.ref('Book', book.id))
}
}
}
}
}
This manually returns the references to the Book
fixtures using utils.ref
(remember that resolvers return references to non-scalar types).
This gives complete control over what is returned by this specific query. However, other fields returning Book
still follow the default behaviour (auto-mocked).
#
2. Specify a mock for id
An alternative is to limit the id generation at the mock level to the predefined set of fixture ids:
import { faker } from '@fakerjs/faker';
const fixtures = {
Book: [...] // same as above
}
const myService = {
typeDefs: '...', // same as above
fixtures, // we moved fixtures in a variable
mocks: {
Book: {
id: () => faker.random.arrayElement(
fixtures.Book.map(b => b.id)
)
}
}
}
With this approach it is impossible for the store to return a reference to any other Book than those defined in fixtures.
It's important to note, that all fields that return Book
will be impacted. This may be preferable, but could also introduce unintended consequences.
The main shortcoming of this second approach is that duplicates may be returned. As id
is generated randomly from the list of ids in fixtures, it's possible to generate the same id twice.
This is most problematic when using lists.
It's also possible to combine these two approaches.
#
What happens with fixtures if a resolver is not specified for a query
If we do not declare a resolver, the default resolver executes for the books
query:
return store.get('Query', 'ROOT', 'books');
The store knows the type of Query.books
is [Book]
and as it's a list, it will generate two entities of type Book
. For each of these, the store will:
- Generate a value for the unique field (
id
), using theBook.id
mock - Insert this book in the store
- Continue resolving other fields from the query
The problem here is that id
is generated as a random Int
via the auto-mock. This random id will probably not be 1
, 2
or 3
, and so will not match any id in the fixtures.