question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Suggestion: Graphql useStaticQuery mocking for tests

See original GitHub issue

Summary

This is a suggestion for mocking graphql queries for unit testing. I’ve used the following pattern in a project to overcome some of the problems that come with the documented way of solving this issue. It can be documented on the website as a solution or integrated into the project.

Basic example

src/components/continue-button.component.js

import React from "react";

export default function ContinueButton () {
   const text = getContinueText();
   return <button>{text}</button>
}

function getContinueText() {
   const data = useStaticQuery(graphql`
   query ContinueButtonText {
      allButtonJson {
         nodes {
           continue
         }
      }
   }
   `);
    return data.allButtonJson.nodes[0].continue;
}
`

src/components/continue-button.component.spec.js

import React from "react";
import { mount } from "enzyme";

import ContinueButton from "./continue-button.component";

describe('ContinueButton Component', function () {
    it(`renders a button saying "Some text"`, function () {
        const component = mount(<ContinueButton />);
        expect(component.text()).toEqual("Some text");
    });
})

__mocks__/queries/continue-button-text.json

{
    "allButtonJson": {
       "nodes": [
          { "continue": "Some text" }
       ]
    }
}

Motivation

I’m working on a project and I need to inject some data from the data layer into some components. Initially I followed the documented way to test it (https://www.gatsbyjs.org/docs/testing-components-with-graphql/#testing-staticquery). I used the Pure<ComponenteName> pattern, however there are a few aspects I heavily disliked about it:

  • The actual code in use is not tested (most important)
  • There is a naming and code tax for every component to be tested. Especially for a simple, small component like the button in the example, I had to write esentially the same code twice (like the documentation example with the Header and PureHeader)
  • I ended using a useStaticQuery call and an if/else clause for returning data during testing:
   const data = useStaticQuery(graphql`
   query ContinueButtonText {
      allButtonJson {
         nodes {
           continue
         }
      }
   }
   `);
   if (data) {
     return data.allButtonJson.nodes[0].continue;
   } else {
      return "Some Text";
   }

Which got very ugly as the returned data became more and more complex.

Implementation

I used the following code to wire up the query responses. I realize the use case is currently limited but I’m sure it can be extended to be more inclusive.

__mocks__/gatsby.js

const React = require("react")
const gatsby = jest.requireActual("gatsby")
const decamelize = require("decamelize");

module.exports = {
   // ... {whatever the docs say}

  graphql: function ([queryString]) {
    return getQueryName(queryString);
  },

  useStaticQuery: function (queryName) {
    const filePath = `./queries/${decamelize(queryName, '-')}.json`;

    try {
      return require(filePath);
    } catch(err) {
      console.warn(`Failed to require mock file for ${queryName}`);
      return {};
    }
  },
}

function getQueryName (queryString) {
  const captureGroup = queryString.match(/query\s([^\s]+).*{/);
  if (captureGroup && captureGroup.length) {
    return captureGroup[1];
  }
}

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:13 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
ghostcommented, Jun 27, 2020

@gtsop Thanks for the mocking pattern. I haven’t tried it but it gives me ideas on how to structure fixtures for individual tests. Regarding the push-back from Blaine, try this and let me know how it works for your use case Mock Gatsby’s useStaticQuery with Jest. I’m also interested to know how things might have changed for your set-up since this issue was closed.

A word on unit testing: If it helps you isolate components that’s a good thing. But if it influences your code too much and you end up writing boilerplate just to be able to unit test that’s an antipattern and so I agree with some of the sentiments in the OP. In particular, your motivational arguments which seem to me to have been misunderstood or taken out of context during this thread. End of the day if Unit Testing isn’t’ easy people aren’t going to do it.

1reaction
gtsopcommented, Feb 13, 2020

@blainekasten

For example, testing the fetching would be a waste of time. That code is ours, and we have tests to ensure it works. If you wrote a test to show that it returns data it would literally never break because we would never ship a breaking change without a major version and an upgrade path.

The unit tests would never test gatsby’s data fetching code simply because that code is mocked during setting up jest, this is out of the question. It’s not about testing the library’s code. It’s about making sure the application code does what it is supposed to do.

As far as the props and data passing. Those are super small integration points that if you failed to do that, your app would break pretty obviously (e.g., the component would crash).

This is an assumption that (apart from being arbitrary and wrong) a library shouldn’t make a decision on.

Why is this assumption wrong? Take this example:

Take this example in which a specific property allows for showing some extra text, a non-breaking feature.

export const PureHeader = ({ data, specialUnusualUserProp }) => (
  <header>
    <h1>{data.site.siteMetadata.title}</h1>
    {specialUnusualUserProp && (<p>Oh you're so special!</p>)}
  </header>
)

export const Header = props => {
  const data = useStaticQuery(graphql`
     ...
  `)
  return <PureHeader {...props} data={data}></PureHeader>
}

Assume I test PureHeader and add a special unit test for asserting the appearance of “Oh you’re so special” text when specialUserProp=true.

Now my intern web-developer goes and modifies the Header component because {...props} looks redundant:

// no props passing :(
return <PureHeader data={data}></PureHeader>

My unit tests pass, my integration tests pass (because I was to bored to re-assert the text thinking I’ve got a unit test for that case) and noone get’s any feedback the application has a broken feature.

The breakage happend because we didn’t unit test application code. It doesn’t matter how trivial it is and it won’t always lead to visible breakage.

But let’s not change the discussion of this thread to “Do I really need to unit test ‘X’ code?” or “What I should have done to avoid this breakage”. Gatsby (and any framework for that matter) should allow for 100% test coverage over the application code, it’s the developer’s decision as to wether they need to test something or not. This decision can’t and shouldn’t be made by the framework.

My solution may be incomplete, limited or on the wrong direction. Thus I accept that closing the issue is ok. However, the underlying problem persists.

And the problem is this: The gatsby-way for unit testing propses writing application code that can’t be unit tested and has 3 different responsibilities:

  • Fetches data
  • Proxies props to another component
  • Passes data to another component
Read more comments on GitHub >

github_iconTop Results From Across the Web

mocking gatsby - Brad Garropy
I went searching on the Gatsby site and found some documentation showing how to mock gatsby . The documentation suggested to use jest...
Read more >
Test mocks - Apollo GraphQL Docs
Generated test mocks provide a type-safe way to mock your response models. Rather than dealing with cumbersome and error prone JSON data, test...
Read more >
Can you have complete coverage when using the pure ...
Recommendation. To achieve code coverage, the most logical thing to do would be to either mock useStaticQuery or better yet use MockProvider ...
Read more >
Testing Components with GraphQL - Gatsby
If you try to run unit tests on components that use GraphQL queries, ... You'll need to mock the useStaticQuery call to test...
Read more >
Testing a Typescript Gatsby app with Jest/Testing Library
Thought I'd ask here: with Typescript and using Testing Library, ... 9 | const data = useStaticQuery(graphql` | ^ 10 | query {...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found