gatsbyjs / gatsby

The best React-based framework with performance, scalability and security built in.
https://www.gatsbyjs.com
MIT License
55.27k stars 10.31k forks source link

Suggestion: Mocking static queries outside of Jest #18449

Closed ascorbic closed 4 years ago

ascorbic commented 5 years ago

I've been thinking a bit about isolating components that use static queries. My particular use case here is Storybooks, but I expect there are other situations where it would be useful to display a component with mock data, such as other component libraries. We've documented how to do this inside tests, by mocking graphql and useStaticQuery or StaticQuery. However outside of a test environment we can't use mocks, leaving us in a catch 22: if we don't use babel-plugin-remove-graphql-queries then we get an error from the graphql calls that haven't been removed. If we do use the plugin, then it expects to be able to use data from the public directory, which may or may not be present. I tried to address with with a PR (#18133) to allow these static queries to be run outisde of a full build. As @pieh correctly pointed out, this is still using production data, which isn't a good idea for Storybooks. It would be much better to allow explicit mock data to be specified.

My suggestion is a specific mock-like implementation, that can be used for both test environments but also component libraries. We'd implement a lightweight mocking library specifically for graphQL, and Gatsby could detect if these library is being used by checking if the mock functions have been called, and if so rather than erroring out on graphql calls, passes them to the mock functions. This library would also include functions that could be called inside the storybook, test etc to allow mock return values to be specified. I've thrown together an illustrative idea of how this library could be implemented and used (pardon my TypeScript). I've tested this by manually replacing the Gatsby imports with imports from this library inside the component, but if implemented properly could be added as a check inside the Gatsby graphql function.

Does this seem like the right sort of approach, or am I on the wrong track?

export type GQLData = { [key: string]: any } | any[];

class GraphQLFaker {
    private stack: GQLData[] = [];
    private fake?: GQLData;
    public useFake = false;

    public getData = (): GQLData | undefined => {
        if (this.stack.length) {
            return this.stack.pop();
        }
        return this.fake;
    };

    public fakeGQLResponses = (data: GQLData, ...args: GQLData[]) => {
        this.useFake = true;
        this.stack.push(data, ...args);
    };

    public fakeGQLResponse = (data: GQLData) => {
        this.useFake = true;
        this.fake = data;
        this.stack = [];
    };
}

const GQLFaker = new GraphQLFaker();

// Gatsby's graphql function could call this to check whether to use mocks
export const shouldUseFakeGQLResponses = () => GQLFaker.useFake;

// Illustrative mock functions. We could add variants of jest's mock functions too

export const fakeGQLResponses = (data: GQLData, ...args: GQLData[]) =>
    GQLFaker.fakeGQLResponses(data, ...args);

export const fakeGQLResponseOnce = (data: GQLData) =>
    GQLFaker.fakeGQLResponses(data);

export const fakeGQLResponse = (data: GQLData) =>
    GQLFaker.fakeGQLResponse(data);

// Mock functions
export const useStaticQuery = <T extends GQLData>(data?: T): T =>
    data || ({} as T);

export const graphql = (query: string | TemplateStringsArray) =>
    GQLFaker.getData();

This could then be used something like this:

import React from "react";

import { storiesOf } from "@storybook/react";
import { fakeGQLResponseOnce } from "../../lib/fakeQL";
import { TweetView } from ".";
import { tweet } from "../fixtures";

fakeGQLResponseOnce({ tweet });
storiesOf("TweetView", module).add("Default", () => <TweetView />);
pieh commented 5 years ago

Before diving into implementation details like that, let's see what are requirements for this mocking system.

I think there is at least few:

ascorbic commented 5 years ago
gatsbot[bot] commented 5 years ago

Hiya!

This issue has gone quiet. Spooky quiet. 👻

We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here.

If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks for being a part of the Gatsby community! 💪💜

github-actions[bot] commented 4 years ago

Hiya!

This issue has gone quiet. Spooky quiet. 👻

We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open! As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks for being a part of the Gatsby community! 💪💜

github-actions[bot] commented 4 years ago

Hey again!

It’s been 30 days since anything happened on this issue, so our friendly neighborhood robot (that’s me!) is going to close it. Please keep in mind that I’m only a robot, so if I’ve closed this issue in error, I’m HUMAN_EMOTION_SORRY. Please feel free to reopen this issue or create a new one if you need anything else. As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks again for being part of the Gatsby community! 💪💜