Mocking data for Storybook
Storybook is a great utility to do isolated development and testing, potentially speeding up development time greatly.
<MockResolver /> enables easy loading of fixtures to see what different network responses might look like. It can be layered, composed, and even used for imperative fetches usually used with side-effect endpoints like create and update.
Setup
- Resource
 - Component
 
export class Article extends Entity {
  readonly id: number | undefined = undefined;
  readonly content: string = '';
  readonly author: number | null = null;
  readonly contributors: number[] = [];
  pk() {
    return this.id?.toString();
  }
}
export const ArticleResource = createResource({
  urlPrefix: 'http://test.com',
  path: '/article/:id',
  schema: Article,
});
export let ArticleFixtures: Record<string, FixtureEndpoint> = {};
import { ArticleResource } from 'resources/ArticleResource';
import ArticleSummary from './ArticleSummary';
export default function ArticleList({ maxResults }: { maxResults: number }) {
  const articles = useSuspense(ArticleResource.getList, { maxResults });
  return (
    <div>
      {articles.map(article => (
        <ArticleSummary key={article.pk()} article={article} />
      ))}
    </div>
  );
}
Fixtures
We'll test three cases: some interesting results in the list, an empty list, and data not existing so loading fallback is shown.
// leave out in production so we don't bloat the bundle
if (process.env.NODE_ENV !== 'production') {
  ArticleFixtures = {
    full: [
      {
        endpoint: ArticleResource.getList,
        args: [{ maxResults: 10 }] as const,
        response: [
          {
            id: 5,
            content: 'have a merry christmas',
            author: 2,
            contributors: [],
          },
          {
            id: 532,
            content: 'never again',
            author: 23,
            contributors: [5],
          },
        ],
      },
      {
        endpoint: ArticleResource.update,
        args: [{ id: 532 }] as const,
        response: {
          id: 532,
          content: 'updated "never again"',
          author: 23,
          contributors: [5],
        },
      },
    ],
    empty: [
      {
        endpoint: ArticleResource.getList,
        args: [{ maxResults: 10 }] as const,
        response: [],
      },
    ],
    error: [
      {
        endpoint: ArticleResource.getList,
        args: [{ maxResults: 10 }] as const,
        response: { message: 'Bad request', status: 400, name: 'Not Found' },
        error: true,
      },
    ],
    loading: [],
  };
}
Decorators
You'll need to add the appropriate global decorators to establish the correct context.
This should resemble what you have added in initial setup
import { Suspense } from 'react';
import { CacheProvider, NetworkErrorBoundary } from 'rest-hooks';
export const decorators = [
  Story => (
    <CacheProvider>
      <Suspense fallback="loading">
        <NetworkErrorBoundary>
          <Story />
        </NetworkErrorBoundary>
      </Suspense>
    </CacheProvider>
  ),
];
Story
Wrapping our component with <MockResolver /> enables us to declaratively control how Rest Hooks' fetches are resolved.
Here we select which fixtures should be used by storybook controls.
import { MockResolver } from '@rest-hooks/test';
import type { Fixture } from '@rest-hooks/test';
import { Story } from '@storybook/react/types-6-0';
import ArticleList from 'ArticleList';
import { ArticleFixtures } from 'resources/ArticleResource';
export default {
  title: 'Pages/ArticleList',
  component: ArticleList,
  argTypes: {
    result: {
      description: 'Results',
      defaultValue: 'full',
      control: {
        type: 'select',
        options: Object.keys(ArticleFixtures),
      },
    },
  },
};
const Template: Story<{ result: keyof typeof options }> = ({ result }) => (
  <MockResolver fixtures={options[result]}>
    <ArticleList maxResults={10} />
  </MockResolver>
);
export const FullArticleList = Template.bind({});
FullArticleList.args = {
  result: 'full',
};