中继:预期会收到一个对象,其中传播了 `...MyComponent_user`,但未找到片段引用`

问题描述

我有一个使用 useFragment 钩子的组件,我试图弄清楚如何传入数据并使用@testing-library/react 对其进行测试:

// @flow
import { graphql,useFragment } from 'react-relay/hooks';
import Field from '../../common/fields/Field.react';
import type { MyComponent_user } from './__generated__/MyComponent_user.graphql';

type Props = {
  user: MyComponent_user,};
export default ({ user: userFragment }: Props) => {
  const user = useFragment(
    graphql`
      fragment MyComponent_user on UserType
 {
        id
        fullName
        userType
        details {
          user {
            id
          }
          address
          city
          state
          zipCode
          country
          phone
          title
          company
        }
      }
    `,userFragment,);

  const userDetails = user?.details;
  const userHref = '/user?userId=';
  return (
    <div className="user-container">
      {user?.fullName && <Field label="Full Name" value={user.fullName} />}
      {user?.userType && (
        <Field
          label="User Type"
          value={user.userType === '1' ? 'Employee' : 'User'}
        />
      )}
      {userDetails?.address && <Field label="Address" value={userDetails.address} />}
      {userDetails?.city && <Field label="City" value={userDetails.city} />}
      {userDetails?.state && <Field label="State" value={userDetails.state} />}
      {userDetails?.zipCode && <Field label="Zipcode" value={userDetails.zipCode} />}
      {userDetails?.country && <Field label="Country" value={userDetails.country} />}
      {userDetails?.phone && <Field label="Phone" value={userDetails.phone} />}
      {userDetails?.title && <Field label="Title" value={userDetails.title} />}
      {userDetails?.company && <Field label="Company" value={userDetails.company} />}
    </div>
  );
};

我正在尝试编写测试:

import { render,screen } from '@testing-library/react';

import { RelayEnvironmentProvider } from 'react-relay/hooks';
import RelayEnvironment from '../../../../environment';

import MyComponent from '../MyComponent.react';

const props = {
  user: {
    id: 1,fullName: 'Meek Mill',userType: '0',details: {
      address: '294 Oak Street',city: 'Philadelphia',state: 'PA',zipCode: '19019',country: 'US',phone: '555-555-5555',title: 'Owner',company: 'Dream Chasers Records',user: {
        id: '237895024906790',},};

describe('when the user type is 0',() => {
  it('displays User as the User Type',() => {
    render(
      <RelayEnvironmentProvider environment={RelayEnvironment}>
        <MyComponent {...props} />
      </RelayEnvironmentProvider>,);

    screen.debug();
  });
});

我收到此错误

image

  Invariant Violation: Relay: Expected to receive an object where `... MyComponent_user` was spread,but the fragment reference was not found`. This is most likely the result of:
    - Forgetting to spread ` MyComponent_user` in `useFragment()`'s parent's fragment.
    - Conditionally fetching ` MyComponent_user` but unconditionally passing a fragment reference prop to `useFragment()`. If the parent fragment only fetches the fragment conditionally - with e.g. `@include`,`@skip`,or inside a `... on SomeType { }` spread  - then the fragment reference will not exist. In this case,pass `null` if the conditions for evaluating the fragment are not met (e.g. if the `@include(if)` value is false.)

      12 |   console.log('userFragment:',userFragment);
      13 |   console.log('props:',props);
    > 14 |   const user = useFragment(
         |                ^
      15 |     graphql`
      16 |       fragment MyComponent_user on UserType {
      17 |         id

如果在文档中添加一些测试示例就好了。

解决方法

在测试 Relay 框架时,您需要了解几件事。

  • 我们需要在 Relay 测试中使用两个主要模块:
    • createMockEnvironment:它提供中继环境接口和模拟层,允许resolvingreject查询的模拟操作,突变和订阅
    • mockPayloadGenerator:允许您为需要测试的组件生成和维护一组模拟数据。
  • 根据您希望在测试中涵盖的测试用例数量,您需要提供一组不同的模拟数据并将其传递给 MockPayloadGenerator

import { render,cleanup } from '@testing-library/react';
import { MockPayloadGenerator } from 'relay-test-utils';

const DEFAULT_PROPS = {
      user: {
        id: 1,fullName: 'Meek Mill',userType: '0',details: {
          address: '294 Oak Street',city: 'Philadelphia',state: 'PA',zipCode: '19019',country: 'US',phone: '555-555-5555',title: 'Owner',company: 'Dream Chasers Records',user: {
            id: '237895024906790',},};

    function TestRenderer(): React.Node {
      const {node} = useLazyLoadQuery<MyTopLevelComponentTestQuery>(
        graphql`
         query MyTopLevelComponentTestQuery
         @relay_test_operation {
           node(id: "TEST_FAKE_USER_ID") {
             ...MyComponent_user
           }
         }
        `,{},);
      return user != null && <MyComponent user={node} />;
    }
    
    describe('MyComponent',() => {
      let environment = createMockEnvironment();

      function renderWithEnvironment(node: React.Node): React.Node {
         return (
           <RelayEnvironmentProvider environment={environment}>
             <Suspense fallback={null}>{node}</Suspense>
           </RelayEnvironmentProvider>,);
      }
 
      function mockNextOperation(props): void {  
        // if you use `resolveMostRecentOperation` you don't need to 
        // have MyTopLevelComponentTestQuery
        // environment.mock.resolveMostRecentOperation(operation =>
        environment.mock.queueOperationResolver(operation =>
        MockPayloadGenerator.generate(operation,{
          UserType() {
            return {
              node: {
                ...props,};
          },}),);
      }

      beforeEach(() => {
       environment = createMockEnvironment();
       cleanup();
      });

     it('when the user type is 0',() => {
       mockNextOperation(DEFAULT_PROPS);
       const {getByText} = render(
         renderWithEnvironment(<TestRenderer />),);
       // whatever your logic is to test your component 
       expect(getByText('User Type: User')).toBeTruthy();
     });

     it('when the user type is 1',() => {
       mockNextOperation(ANOTHER_TEST_PROPS);
       const {getByText} = render(
         renderWithEnvironment(<TestRenderer />),);
       // whatever your logic is to test your component
       expect(getByText('User Type: Employee')).toBeTruthy();
     });
  });