Fishery in React Native Applications

Tobi Shokunbi

Fishery was announced by thoughtbot in February 5, 2020. This library was created with the aim of setting up JavaScript objects for use in tests and anywhere else you need to set up data. Fishery is influenced by our popular Ruby factory library, factory_bot.

Fishery is a very good tool when writing tests in your React Native application. It enables you to;

  • Build typed objects
  • Accept typed parameters
  • Return typed objects

Fishery is built with TypeScript in mind, and should you find yourself in a place where you don’t use TypeScript, that’s fine too. Fishery still works, just without the extra type-checking that comes with TypeScript.

Exploring Fishery

Let’s try out some examples in our React Native project, We will be writing some basic tests using Jest and applying Fishery. Before we begin we’d need to set up Fishery in our React native project, so let’s do that.

Setting up Fishery

First, install fishery.

npm install --save fishery

or

yarn add fishery

Now we have Fishery setup, let’s work on our component.

Creating our React Native Component

We are going to be creating a View component that accepts an object containing details of a user (eg first and last name, etc.) and then displays the user’s full name.

Our component will look like this.

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

interface UserDetailsProps {
    firstName:string,
    lastName:string
}
export default function UserDetails(props:UserDetailsProps) {

    return (
        <View style={styles.container}>
            <Text testID="test">{props.firstName} {props.lastName}</Text>
        </View>
    );
}
const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
    },
});

The component Userdetails is a component that will return the user’s firstName and lastName, this component can be used all over the app when we need to show the user’s data. We also included a testID that will be needed when we begin to write the test.

This now provides us with an opportunity to write some test, we can test :

  • Does the component have a text showing firstName & lastName.
  • Does the component even renders.

Let’s write some tests, to do so, we’d need to create a new file called userDetails.test.tsx. This file will contain all test that has to do with the UserDetails component.

Our test without Fishery will look like this

import React from "react";
import { render, RenderAPI } from "react-native-testing-library";

import UserDetails from "./userDetails";
describe('Test user detail component', () => {

  test('Displays users full name', () => {

    let c: RenderAPI;

    const UserDetailsProps = {
      firstName: 'Tobi',
      lastName: 'Shokunbi'
    }

    const fullName = `${UserDetailsProps.firstName} ${UserDetailsProps.lastName}`

    c = render(
      <UserDetails
      lastName={UserDetailsProps.lastName}
      firstName={UserDetailsProps.firstName}
    />)

    expect(c.getByTestId('test')).toHaveTextContent(fullName);
  })
});

So what are we doing here?

  • Defining the test to Display user full name.
  • Created our own mocked props for the UserDetails component, called userDetailsProp.
  • We define what the full name should be in constant fullName
  • We render the <UserDetails> component.
  • lastly, we check that our Text component with an id of test has content of the user’s fullName constant.

This is great but scales poorly, especially when our test begins to get more complex, So now let’s try to re-write this test but using Fishery.

Our test with Fishery will look like this

We’d need to define a user factory, to do this, you’d need to create a new file factories/user.ts

// factories/user.ts
import { Factory } from 'fishery'; 

type User = {
  firstName: string,
  lastName: string
}
const userFactory = Factory.define<User>(({ sequence }) => ({
  id: sequence,
  firstName: 'Tobi',
  lastName: 'Shokunbi'
}));
export default userFactory;

Now we have created a factory that will help us create the needed data structure to run our test, we can now proceed to re-writing the test.

describe('Test user detail component // Using Fishery', () => {

  test('Displays users full name', () => {
    let c: RenderAPI;

   const UserDetailsProps = userFactory.build({
      firstName:'Tobi',
      lastName: 'Shokunbi'
    })

    const fullName = `${UserDetailsProps.firstName} ${UserDetailsProps.lastName}`

    c = render(
      <UserDetails
      lastName={UserDetailsProps.lastName}
      firstName={UserDetailsProps.firstName}
    />)

    expect(c.getByTestId('test')).toHaveTextContent(fullName);
  })
});

with the introduction of Fishery in our test, it becomes easy to create objects needed for testing our UserDetails component. This also allows us to type the object, so we’re sure of the type of data.

Conclusion

Fishery begins to shine when your project gets larger, Fishery allows you to ship with confidence, keep it DRY, and allows for a lot of code reuse. This project is available here on GitHub if you’d like to play with the code.