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 oftest
has content of the user’sfullName
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.