J. Hesters
J. Hesters

Reputation: 14806

How to test that the renderItem function returns a <ListItem />?

I'm building my app with React Native and I do my unit tests with Jest and Enzyme. How can I test my <FlatList />'s renderItem() function?

It returns a <ListItem /> from the React-Native-Elements library.

Let me give you the example code:

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}
      />
    )
  }
}

I would like to be able to test the renderItem() function. My problem is, that wrapper.instance().renderItem({item: item}) returns the error: TypeError: wrapper.instance(...).renderItem(...).find is not a function. Let me give you the code of the test that I wrote:

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);
      });
    });
  });
});

How would I have to write this test so that I can test if the function correctly renders a <ListItem />?

Upvotes: 9

Views: 17470

Answers (4)

Denys Makhov
Denys Makhov

Reputation: 382

You can also test using the renderProp function.

const wrapper = shallow(<YourComponent {...defaultProps} />);
const flatList = wrapper.find('FlatList');

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

Upvotes: 4

Huy Nguyen
Huy Nguyen

Reputation: 2060

You can test FlatList with react-native-testing-library

Here's example:

Components:

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>
    )
  }
}

Unit testing:

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)
    })
})

Hope this help!

Upvotes: 8

Brian Adams
Brian Adams

Reputation: 45830

renderItem() returns a JSX element. JSX compiles to React.createElement() which returns an object.

Therefore, the return value from renderItem() is just an object.

You can test that renderItem() creates the correct object by doing the following:

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'
    }
  });
});

Upvotes: 8

jimjkelly
jimjkelly

Reputation: 1661

So I'm not sure whether you've extracted some code out of a class-based component, but renderItem itself is a component. I can maybe give you some test code for that and you can adapt it for your needs, assuming you imported shallow and set up your item variable:

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

There are two key things that are different from your example. One - I assume here you've imported ListItem into your test. You can then pass that directly into find. The second bit is you want to pass the result of finding and checking the length into expect and test the value of that. Think of it is "what I want to test" (the number of ListItem components it can find) and then off of that you create your assertion (toEqual(1)).

Additional Information to Reflect Question Edit

In your setup, I wouldn't bother to test renderItem directly. Instead I'd ensure full testing of ListItem, and then assert things about how MyList renders FlatList. This can be done using expect(wrapper).toMatchSnapshot(), or even better by asserting some things about the props given to FlatList. If you are really paranoid about all of this, perhaps use mount instead of shallow to render it completely.

Upvotes: 0

Related Questions