Jest – think integration

I want to use an example form component that uses react-hook-form, you can see it featured in my recommended packages post here.

Let’s start by creating our form component.

And now FirstField.tsx

Let’s just assume that the implementation of SecondField is much the same as FirstField, but without the useEffect, and for the purpose of this post we will only care about unit testing FirstField.

I have witnessed people writing unit tests for forms/ fields like these that mock imports like useFormContext and useWatch, but this is not the best way. By doing this we might completely lose the essence of the library, and end up writing tests and do not reflect real world. What we should try to do is incorporate as much of this form eco system in our tests. Let’s take a look at how we could do this:

Ok looking better, so let’s think about the behaviour of our FirstField component, in terms of testing the form eco system, we need to check the following:

  1. input will load with a value if one already exists
  2. input will change its own value when interacted with
  3. the input value will change in to ‘Frodo’ if field secondField has value of ‘Sam’

Well we have simple and real ways of checking this now, and no synthetic forcing of “user behaviour” via complicated mocks is required. Lets take scenario one, we could easily change our wrapper so it takes a defaultValues object, and instead of calling useForm like this:

We can simply do this:

and now we would be able to assert on whether our input loads up with a value that already exists for the field it subscribes to.

The second scenario is quite simple, we could use use user-event to type something into the field, and make sure the value changes.

The third scenario is interesting, because it relies on a field that is not part of our unit, and let me start by saying that we should not import SecondField, because then the unit tests for FirstField would be coupled with the component SecondField. I once wrote a helper utility to abstract away the wrapping of RHF in field unit tests, and I would often get the request “can you, or can I, extend the helper to expose the setValue of the context” and my answer would always be “well I don’t think we should do that”. This is why, simply put…it does not mimic real world. In the tests we should be trying to get as close to real user behaviour as we can, and in my opinion, forcefully calling setValue here without any interaction from an input isn’t achieving that. So what can we do? Well let’s consider this…what if we can pass an array of additional field identifiers to our helper, then we can generate these additional fields inside our FormProvider on a per test basis.

Now we can use user-event to type something in that field and assert on whether our firstField input is updated. There are lot’s of other great things you can do here, how about a helper that checks for a successful form submission? In our wrapper we can pass some Jest mocks to the submission like so:

and then we can add helper that knows which has happened after a submission.

then in our unit test we can have an assertion that looks like this: