Reading:
How to Contract Test with GraphQL

How to Contract Test with GraphQL

Updated

The results from our State of Software Quality: API  2023 are in and it’s a multi-protocol world.

While REST continues to dominate, 23% of respondents claim to actively use GraphQL. This follows the multi-protocol trend we’ve observed for years now. There’s no doubt in our minds that adoption for GraphQL will continue to grow YoY because of its many benefits.

If you’re new to the contract testing game, GraphQL or just need a refresher, follow along as we dive deeper into contract testing for GraphQL.

What is GraphQL?

GraphQL is a query language for APIs, enabling declarative data fetching from a variety of sources such as a database or another API. Unlike REST APIs, which requires users to stitch multiple resources together, GraphQL retrieves all required data in one request, using types and fields instead of individual endpoints with very specific data models.

GraphQL services can be implemented in any programming language, while the GraphQL schema language ensures consistency in a language-agnostic manner. Schemas define object types, each representing a fetchable object with associated fields. This offers a familiar structure for developers.

For example, the following GraphQL Schema shows how you might query to get a specific Product:

type Product { 

    id: ID! 

    name: String! 

    type: String! 

    price: Int! 

} 

  

type Query { 

    product(id: ID!): Product 

}


Each field on a type is backed by a resolver function, provided by the GraphQL server developer. When a field is executed, the corresponding resolver generates the next value. This approach streamlines back-end development by eliminating the need for writing specific responses or versioning APIs. Front-end teams benefit from requesting only the required data, enhancing both back-end and front-end performance.

What are the benefits of using GraphQL?

GraphQL offers several key benefits for developers and applications. One of the primary advantages is its flexibility, allowing clients to request precisely the data they need, with less over-fetching and under-fetching of information. This results in more efficient and faster data retrieval, improving the performance of applications.

Additionally, GraphQL promotes a strong type system, offering auto-generated documentation, making it easier to understand the available data and operations. Overall, GraphQL enhances the developer experience, optimizes data transfer, and enables a more streamlined and responsive approach to building modern, data-driven applications.

The case for contract testing GraphQL

Contract testing is a software testing approach that focuses on the interactions between different software components to ensure they can communicate effectively. It does so by capturing a contract, which records the specific details of their interactions – such as HTTP requests and responses – and the scenarios in which they are valid.

You may then ask what is the value of contract testing GraphQL, which itself has a form of contract already – the GraphQL Schema?

Whilst this is a good point, there are several reasons to consider contract testing for GraphQL to prevent production issues:

  • It doesn’t prevent breaking changes– the GraphQL schema can change on the server side and any clients expecting the previous behaviour will be broken.

  • Schemas are abstract and insufficient to describe API semantics– which can lead to incorrect assumptions about how the API is supposed to work.

  • GraphQL’s deprecation feature is at runtime – too late to notify any users of the API!

Contract testing with Pact resolves this, as each scenario is made explicit via the “specification by example” approach.

Here are some of the benefits of using contract testing for GraphQL APIs:

  • Gain visibility into consumers: knowing who your consumers are and what they need gives you greater freedom to evolve your API

  • Increased confidence: prevent releasing breaking changes to users, before you commit

  • Improved communication: by making scenarios explicit, you can improve communication between developers who are working on different GraphQL APIs (This is because contract tests provide a common reference point for understanding the expected behavior of the APIs)

  • GraphQL often relies on HTTP services downstream, so adding contract tests in these layers will improve confidence all of the way down the stack

How to test your GraphQL using PactFlow

It turns out contract testing GraphQL is not a lot different than testing other HTTP services, as GraphQL is just an abstraction over HTTP.

1. Install Pact client language SDK

Pact supports over 10 languages., iIn this example, we will use Java. See https://github.com/pact-foundation/pact-jvm for installation instructions.

2. Write consumer test

The consumer and provider can be implemented in any programming language. The consumer will need to be able to send GraphQL queries and mutations, and the provider will need to be able to respond to GraphQL queries and mutations.

@ExtendWith(PactConsumerTestExt.class) 

@PactTestFor(providerName = "graphql-api") 

public class ProductsPactTest { 

  @Pact(consumer="graphql-consumer") 

  public RequestResponsePact getProduct(PactDslWithProvider builder) { 

    // Arrange: establish the mock and test conditions 

    PactDslJsonBody body = new PactDslJsonBody(); 

    body 

      .object("data") 

        .object("product") 

          .stringType("id", "10") 

          .stringType("name", "product name") 

          .stringType("type", "product series") 

        .closeObject() 

      .closeObject(); 

    final String query = """ 

      { 

      "query": "{ 

        product(id: 10) { 

          id 

          name 

          type 

        }} 

      "} 

      """; 

    return builder 

      .given("a product with ID 10 exists") 

      .uponReceiving("a request to get a product via GraphQL") 

        .path("/graphql") 

        .headers(Map.of("content-type", "application/json")) 

        .method("POST") 

        .body(query) 

      .willRespondWith() 

        .headers(Map.of("content-type", "application/json")) 

        .status(200) 

        .body(body) 

      .toPact(); 

  } 

  @PactTestFor(pactMethod = "getProduct") 

  @Test 

  public void testGetProduct(MockServer mockServer) throws IOException, URISyntaxException { 

    // Act: call the API client we are testing 

    Product product = new ProductClient().setUrl(mockServer.getUrl()).getProduct("10"); 

  

    // Assert: perform any other relevant unit testing checks, such as the product unmarshaling correctly 

    assertThat(product.getId(), is("10")); 

  } 

}

3. Verify the GraphQL API (Provider)

Now that we have our contract file (locally), let’s validate it. To do this, we simply need to start our GraphQL API on our machine, and tell Pact:

  1. Where the API is running (here, on port 8080)
  2. Where to find the pact file to validate (a local directory)
@ExtendWith(SpringExtension.class) 

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) 

@PactFolder("path/to/pact/directory/from/step/1") 

class ProductsPactTest { 

  @Autowired 

  ProductRepository repository; 

  @BeforeEach 

  public void setupTestTarget(PactVerificationContext context) { 

    context.setTarget(new HttpTestTarget("localhost", 8080)); 

  } 

  @TestTemplate 

  @ExtendWith(PactVerificationInvocationContextProvider.class) 

  public void pactVerificationTestTemplate(PactVerificationContext context) { 

    context.verifyInteraction(); 

  } 

  @State("a product with ID 10 exists") 

  public void setupProductX010000021() throws IOException { 

    System.out.println("a product with ID 10 exists"); 

    repository.save(new Product(10L, "test", "product description", "1.0.0")); 

  } 

} 

That’s it – we have now performed a basic GraphQL contract testing. To do it properly, you should integrate this with PactFlow and into your CI/CD pipeline.

To learn more, try out our GraphQL example or check out our documentation.

Avoid FOMO, try it out now

GraphQL is a powerful and flexible API query language that offers a number of benefits for both developers and users. It is a good choice for building a wide variety of applications, from simple websites to complex enterprise systems. As microservices architecture continues to increase in demand, so will GraphQL’s popularity.

Ready to start contract testing your GraphQL? Try PactFlow for free now.

Integrate Deeper with our Developer API 🧿
22 November 2023

Integrate Deeper with our Developer API 🧿

Announcing the launch of PactFlow’s Developer API.

3 min read

Image
By Matt Fellows
Farewell Candy: Levelling Up Beyond Our Team!
25 August 2023

Farewell Candy: Levelling Up Beyond Our Team!

Today, we're hitting a unique combo of emotions – a mix of joy for what lies ahead and a tinge of sadness for what's coming to an end.Our wonderful Candy, the young and

1 min read

By Ilia Mogilevsky
arrow-up icon