Testing with Apollo Federation
Unit, integration, end-to-end, composition, and component and operation testing
💡 TIP
If you're an enterprise customer looking for more material on this topic, try the Enterprise best practices: Testing course on Odyssey.
Not an enterprise customer? Learn about GraphOS for Enterprise.
Testing in GraphQL may seem like it can involve more steps, but that is because your GraphQL architecture can involve many areas of your tech stack, from your frontend with Apollo Client to a backend with Apollo Server, or your infrastructure with your GraphOS Router to the individual subgraphs in your supergraph. In practice, all these areas should be properly tested the same way if we were using any other API technology, but often the testing across boundaries like teams or applications can involve some new steps. By the end of this guide you should have:
- Unit testing in individual subgraphs
- Integration testing for the individual subgraphs
- End-to-end testing for your entire supergraph
- Composition testing for supergraph schema generation
- Component and operation testing for your clients
Unit testing
We recommend creating unit tests for each of your subgraph server resolvers. Resolvers are the code that gets called for each type or field in your schema, which creates a natural boundary to test in isolation. When doing so, we recommend mocking as much data as possible using a package like @faker-js/faker
. This package generates realistic fake data for mocking inputs and outputs.
Using @faker-js/faker
to mock a return value in jest
looks something like the following:
import {faker} from '@faker-js/faker';const testUser = {userId: faker.datatype.uuid(),username: faker.internet.userName(),email: faker.internet.email(),avatar: faker.image.avatar(),password: faker.internet.password(),birthdate: faker.date.birthdate(),registeredAt: faker.date.past()};const mockedFunction = jest.fn().mockReturnValue(testUser);
Reference resolvers in unit tests
The __resolveReference
function (also known as a reference resolver) enables different subgraphs to resolve the fields of a federated entity. They are also just functions, making them a good boundary to unit test with mocks. Reference resolvers are vital to the successful execution of federated operations, so they are important to validate.
Integration testing
Integration tests for subgraphs should start up a single subgraph and send operations to the schema in a mocked or test environment. To test just the subgraph in isolation, validate all the fields giving special focus to the top-level fields in your queries and mutations, and use all permutations of your inputs to check your schema matches your resolvers.
Entity resolvers in integration tests
Depending on your schema and operations, the resulting query plan may fetch data from a subgraph using the entity resolvers. The integration tests in this situation involves mimicking the gateway/router. You can execute a query against _entities
to do integration tests and your test cases should cover all the entity types that can be resolved by the individual subgraph (all the types marked with @key
).
This looks like the following:
query GetEntities($representations: [_Any!]!) {_entities(representations: $representations) {... on User {firstName}}}
with the following input
{"representations": [{"__typename": "User","id": "5"}]}
To see more examples on how to test these operations, see Query._entities
.
End-to-end testing
Follow these best practices when creating end-to-end tests for your supergraph:
- Run all your subgraphs and router in a test environment with either mocked or test data
- Use example operations that are actually executed against your supergraph.
- You can view the details of recent operations in GraphOS Studio.
- Avoid boilerplate or randomly generated operations, because these don't reflect actual traffic.
- If you are not in production yet, we suggest making these tests as close to what you think they will be as possible.
- Make sure to include operation that span multiple subgraphs to validate entity resolvers
- Use variables to ensure high operation cardinality.
- If your test operations don't use any GraphQL variables (or if you use the same variable values across executions), your supergraph is likely to return cached data. This circumvents large portions of execution logic, limiting test effectiveness.
- By using a variety of operations and variable values, you help make sure that your tests result in minimal cache hits.
Composition testing
Composition testing is specific to a federated architecture. It involves testing that your subgraph schemas successfully compose into a supergraph schema that can resolve the operations sent by clients. You perform these tests with the Rover CLI, via the rover subgraph check
command. We recommend performing this check in your CI pipeline as part of code-reviews and in your CD pipeline to confirm the changes you are about to deploy are still valid since the last time you ran the check.
Component and operation testing
Fortunately, clients are not actually aware of when they are using a Federated GraphQL API versus a non-Federated one so the testing best practices remain the same. Our schema can provide a convenient layer to mock our data fetching, or for individual components you can mock the specific client providers that inject or fetch your GraphQL data: