# 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.

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:

  1. Generate a value for the unique field (id), using the Book.id mock
  2. Insert this book in the store
  3. 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.