2
votes

I've recently starting programming. I'm on a team who is programming in React and is using Enzyme, Mocha and Chai for unit testing. See package versions below.

When testing a component where there are several use cases that require different prop values, should I use beforeEach() and then setProps() in each test, or should I do an explicit mount() (or shallow()) at the start of each test? Does it matter?

For example, I could use beforeEach() to mount without any props and then use setProps() in each test like this (using pseudocode):

describe('MyComponent', () => {

   beforeEach(... let component = mount(<MyComponent />) ...)

   it('tests use case 1', () => {
      // set prop A = 123
      component.setProps({A: 123})
      // assert some stuff
   })

   it('tests use case 2, () => {
      // set prop A = 456 and B = 'foo'
      component.setProps({A: 456})
      component.setProps({B: 'foo'})
      // assert some stuff
   })

})

or I could do a use-case specific mount at the start of each test, passing in props in the mount, like this:

describe('MyComponent', () => {

   it('tests use case 1', () => {
      ...mount(<MyComponent A=123 />)
      // assert some stuff
   })

   it('tests use case 2, () => {
      ...mount(<MyComponent A=456 B='foo' />)
      // assert some stuff
   })

})

What are the pros and cons of each method? Is there a best practice?

Packages

  • "enzyme": "^3.3.0",
  • "enzyme-adapter-react-16": "^1.1.1",
  • "mocha": "^5.0.0",
  • "chai": "3.5.0"
1
I don't think you can set the props after mounting like in your first scenario, so if the props are different per test, the second variant is the way to goPatrick Hund
@PatrickHund In beforeEach, the mount() result is assigned to a variable. E.g., let component = mount(...). Then in each test case, you can do component.setProps({A: 123}). This seems to work (i.e., the assertions pass), but have we misunderstood something?Marnie A.
You're right, I wasn't aware you could do this with Enzyme. I learned something, thx :-)Patrick Hund
@MarnieA. When you do setProps it will execute the update life cycle of the component, it's not same as passing the props initially. So when you want to test the componentDidMount/useEffect with some dependency, you won't be able to do with setProps. I personally prefer the 2nd approach with a factory method as it's cleaner and everything that the other reader needs to understand is at one place. Here is a nice article by Kent.C.Dodds on such patterns. kentcdodds.com/blog/test-isolation-with-reactHardik Modha

1 Answers

0
votes

For class components there is componentDidMount and constructor while for functional components there is useEffect(..., []). All that things are called just once.

On the other side for approach #2 it's still needed to test props update in separate test case to ensure component handles that properly. Otherwise you may miss the case when say using the same component in different <Route> does not fetch data on navigation(that happen in componentDidMount only)

Say if you have

<Route path="/Album/:id/author" component={UserScreen} />
<Route path="/user/:id/" component={UserScreen} />

and if you can navigate directly from first to second it means React-Router will not re-create UserScreen but just update instance already rendered. So with approach #1 you would cover this case with tests automatically. While approach #2 will need you testing componentDidUpdate behavior explicitly.

I'm not sure what's better but want to highlight that difference may happen between testing flow and real-project-flow.