cube-js / cube

📊 Cube — Universal semantic layer platform for AI, BI, spreadsheets, and embedded analytics
https://cube.dev
Other
18.02k stars 1.78k forks source link

Improve documentation for Testing within React & Jest useCubeQuery #7507

Open joaobibiano opened 11 months ago

joaobibiano commented 11 months ago

Is your feature request related to a problem? Please describe.

As with any codebase, testing is very important. By using Cube for the first time I reached the point where I had to mock the HTTP layer to be able to test my code as a whole thing (integration-test).

code I wanted to test

const EmailPerformanceEngagementChart = () => {
  const { filters } = useAnalyticsContext();
  const context = useContext(UserContext);
  const {
    absoluteTimeRange: { startDate, endDate },
  } = filters;

  const { error, isLoading, resultSet, refetch } = useCubeQuery({
    filters: getFilters({
      accountId: context.accountId as string,
      mailboxEmails: filters.mailboxEmails,
      userEmails: filters.userEmails,
    }),
    measures: ['sent_emails.total_count'],
    timeDimensions: [
      {
        dimension: 'sent_emails.date_sent',
        granularity: 'day',
        dateRange: [startDate.toISOString(), endDate.toISOString()],
      },
    ],
    order: {
      'sent_emails.date_sent': 'asc',
    },
  });

  if (error) {
    return <GeneralError onRetry={refetch} />;
  }

  if (isLoading) {
    return <LineChartLoading />;
  }

  return (
    <div>
      <LineChart
        seriesNames={resultSet?.seriesNames()}
        data={resultSet?.chartPivot()}
        xAxisDataKey={({ x }) => {
          return new Date(x).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
        }}
      />
    </div>
  );
};

export default EmailPerformanceEngagementChart;

Describe the solution you'd like

Then I saw that cubejs instance accepts an HTTP transport implementation. At first glance I thought of implementing it with Axios, because I already have the setup for mocking and testing it, but after careful consideration, I find out that could be a little bit risky as the Cube team could change something there and I miss something besides being using typescript.

Describe alternatives you've considered

Then, after reading the Cube HTTP implementation I saw they were using cross-fetch which is a "proxy" for multi-environment javascript execution. So the solution became simpler (although I still think this could be different, for example by using jest-fetch-mock but I wasn't able to make it work.)

my test file

  import crossFetch from 'cross-fetch';

  jest.mock('cross-fetch', () => {
    return {
      __esModule: true,
      default: jest.fn(),
    };
  });

  it('renders', async () => {
      crossFetch.mockResolvedValue({
        status: 200,
        text: () => Promise.resolve(JSON.stringify(engagementChartMockData)),
      });

      const { container } = render(<Container />);

      const horizontalAxisValues = 8;

      await waitFor(() => {
        const axisValues = container.querySelectorAll('.recharts-cartesian-axis-tick-value');
        expect(axisValues).toHaveLength(horizontalAxisValues);
      });
    });

This isn't bullet proof because I want to be able to mock specific endpoints by defining their methods and queries, but it solves my problem for now.

When having multiple queries inside your component, you probably will need to mockImplementation

end

Hope this can help someone and maybe get some answers from the Cube team about testing practices in their documentation

github-actions[bot] commented 11 months ago

If you are interested in working on this issue, please leave a comment below and we will be happy to assign the issue to you. If this is the first time you are contributing a Pull Request to Cube.js, please check our contribution guidelines. You can also post any questions while contributing in the #contributors channel in the Cube.js Slack.