如何测试renderItem函数返回一个<ListItem />?

9

我正在使用React Native构建我的应用程序,并使用Jest和Enzyme进行单元测试。如何测试我的<FlatList />renderItem()函数?

它返回来自React-Native-Elements库的<ListItem />

让我给你一个示例代码:

import { ListItem } from 'react-native-elements'

export class MyList extends Component {
  const list = [
    {
      name: 'Amy Farha',
      subtitle: 'Vice President'
    },
    {
      name: 'Chris Jackson',
      avatar_url: 'https://s3.amazonaws.com/uifaces/faces/twitter/adhamdannaway/128.jpg',
      subtitle: 'Vice Chairman'
    },
    ... // more items
  ]

  keyExtractor = (item, index) => index

  renderItem = ({ item }) => (
    <ListItem
      title={item.name}
      subtitle={item.subtitle}
      leftAvatar={{
        source: item.avatar_url && { uri: item.avatar_url },
        title: item.name[0]
      }}
    />
  )

  render () {
    return (
      <FlatList
        keyExtractor={this.keyExtractor}
        data={this.state.dataSource}
        renderItem={this.renderItem}
      />
    )
  }
}

我希望能够测试renderItem()函数。我的问题是,wrapper.instance().renderItem({item: item})返回错误:TypeError: wrapper.instance(...).renderItem(...).find is not a function。让我给你我写的测试代码:

describe("component methods", () => {
  let wrapper;
  let props;
  let item;
  beforeEach(() => {
    props = createTestProps();
    wrapper = shallow(<MyList {...props} />);
  });

  describe("renderItem", () => {
    describe("user", () => {
      beforeEach(() => {
        item = {
          name: 'Chris Jackson',
          avatar_url: 'https://s3.amazonaws.com/uifaces/faces/twitter/adhamdannaway/128.jpg',
          subtitle: 'Vice Chairman'
        };
      });

      it("should display the order as a <ListItem />", () => {
        expect(
          wrapper
            .instance()
            .renderItem(item)
            .find("ListItem")
        ).toHaveLength(1);
      });
    });
  });
});

我该如何编写此测试,以便测试函数是否正确呈现<ListItem />

4个回答

8

你可以使用react-native-testing-library来测试FlatList组件。

以下是一个示例:

组件:

const Item = ({ name ) => <Text>{name}</Text>

class LisItem extends React.Component {
  _keyExtractor = (item: { id: string }) => item.id

  render() {
    return (
      <View style={styles.container}>
        {todos && (
          <FlatList
            data={this.props.todos}
            keyExtractor={this._keyExtractor}
            renderItem={({ item: { id, name } }) => (
              <Item
                key={id}
                name={name}
              />
            )}
          />
        )}
      </View>
    )
  }
}

单元测试:

import { render } from 'react-native-testing-library'

const mockDataTodos = [
  {
    id: 'id-1',
    name: 'Todo-1',
  },
  {
    id: 'id-2',
    name: 'Todo-2',
  },
  {
    id: 'id-3',
    name: 'Todo-3',
  },
]

describe('Testing FlatList', () => {
    test('Error component should be render when error is true', () => {
      const componentTree = render(
        <ListItem todos={mockDataTodos} />,
      )

      expect(componentTree.getAllByType(FlatList).length).toBe(1)
      expect(componentTree.getAllByType(Item).length).toBe(mockDataTodos.length)
    })
})

希望这能帮到你!

2
关于 getAllByType 的注意事项:“它们的使用不被鼓励”,https://callstack.github.io/react-native-testing-library/docs/migration-v2/#removed-functions - pmiranda
使用'findAllByType()'代替getAllByType。 - Someone Somewhere

8

renderItem() 返回一个JSX元素。 JSX编译为返回对象的React.createElement()

因此,renderItem() 的返回值只是一个对象。

您可以通过以下方式测试renderItem() 是否创建了正确的对象:

it("should display the order as a <ListItem />", () => {
  const element = wrapper
    .instance()
    .renderItem(item);
  expect(element.type).toBe(ListItem);
  expect(element.props).toEqual({
    title: 'Chris Jackson',
    subtitle: 'Vice Chairman',
    leftAvatar: {
      source: { uri: 'https://s3.amazonaws.com/uifaces/faces/twitter/adhamdannaway/128.jpg' },
      title: 'C'
    }
  });
});

1
这是一个很好的答案。 - Kasra

4
你可以使用renderProp函数进行测试。
const wrapper = shallow(<YourComponent {...defaultProps} />);
const flatList = wrapper.find('FlatList');

const item = flatList.renderProp('renderItem')({ item: yourData });
expect(item).toMatchSnapshot();

0

所以我不确定您是否从基于类的组件中提取了一些代码,但是renderItem本身就是一个组件。我可以为此提供一些测试代码,您可以根据自己的需求进行调整,假设您已经导入了shallow并设置了item变量:

describe('renderItem', () => {
   it('should display as a ListItem', () => {
       const wrapper = shallow(<renderItem item={item} />);
       expect(wrapper.find(ListItem).length).toEqual(1);
   });
});

有两个关键点与您的示例不同。一 - 我在这里假设您已经将 ListItem 导入到您的测试中。然后,您可以直接将其传递给 find。第二个关键点是您想要将查找和检查长度的结果传递给 expect 并测试该值。将其视为“我想要测试的内容”(可以找到的 ListItem 组件的数量),然后基于此创建断言(toEqual(1))。

反映问题编辑的附加信息

在您的设置中,我不会费心直接测试 renderItem。相反,我会确保完全测试 ListItem,然后断言关于 MyList 如何呈现 FlatList 的事情。这可以使用 expect(wrapper).toMatchSnapshot() 来完成,甚至更好的方法是断言一些关于传递给 FlatList 的 props 的内容。如果您真的对所有这些都很担心,也许使用 mount 而不是 shallow 来完全渲染它。


@jimkelly 谢谢你的帮助。No renderItem 不是一个组件本身。正如您所看到的,我编辑了我的问题以反映这些更改。renderItem 是来自 <MyList /> 的组件方法。 - J. Hesters
1
@jimkelly 谢谢你提供的额外信息!请允许我介绍一下我的背景。我来自于 Python 编程,其中有一句话:“显式优于隐式。”因此,我喜欢避免使用 toMatchSnapshot(),因为我认为它会促进不良实践(仅隐式检查代码是否已更改,而不是明确地测试代码)。mount 可能是一个值得研究的好方法。感谢你的帮助! - J. Hesters
这个不起作用。你可能误解了问题。 - Kasra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接