DEV Community

Discussion on: The religion of test-driven development

Collapse
 
0916dhkim profile image
Danny Kim

It makes better sense now. I, too, mostly use Testing Library with Jest, but my problem has been that I always tried to make my tests smaller. Let me try writing more integration tests than unit tests and see how it goes. Unlike your workflow, I usually create a small independent component with its unit tests and put the new component inside existing components. I guess my tendency to write smaller tests made them more "brittle."

Anyway, here are two objectives I think about when I write my tests:

  1. Does the component reflect the state correctly?
  2. Do user interactions have correct consequences?

1 is easy to test, but it can be difficult to setup when the state is external (not props) or complicated. I mock external data.

2 is more tricky to test IMO. Many side-effects must be mocked (e.g. data fetching & url redirection). Also, querying for HTML elements is not trivial; I often end up with parent <div> when I actually wanted to select the child <input>. Querying is difficult for me because I am writing my tests for components which do not exist yet. Today, I wrote a test for a React form that goes like this: "fill out two text fields, select an option from a dropdown, press a button then check if the form sends a correct request." I spent more time troubleshooting getByRole() calls and fireEvent calls than implementing the form.

What I love about TDD is the confidence it gives me during development process. Before implementation, I have a firm objective to aim for, and after implementation, I am assured that my implementation is tested; however, writing solid tests has been a real challenge for me when it comes to frontend. Maybe it will be alright after I get more used to ARIA roles and web in general.

Thank you for your input. Now I feel like I have a better strategy to try out.

Thread Thread
 
frontendphil profile image
Philipp Giese

Glad I could help. Maybe look at it like this. If it's hard for you to write a test then it will also be hard for people with a screen reader to navigate your app. This motivated me a lot to get more comfortable with accessibility.

What you're describing looks like this to me:

const { getByLabelText, getByRole } = render(<Form />)

fireEvent.change(getByLabelText('Text input'), { 
  target: { 
    value: 'input value' 
  }
})

fireEvent.focus(getByLabelText('Dropdown'))

fireEvent.click(getByRole('option', { 
  name: 'The option you want to select' 
}))
Enter fullscreen mode Exit fullscreen mode

Accessing the request is a tricky thing. However, we can make this more accessible in our tests as well. For instance, we've created a custom render method that fits our codebase. So when we use our custom render we can do more things:

const { getFetchRequest, getByRole } = customRender(<Form />)

fireEvent.click(getByRole('button', {
  name: 'Submit'
})

const request = getFetchRequest(SUBMIT_REGISTRATION)

expect(request.body).toHaveProperty('emailAddress', 'foo@bar.com')
Enter fullscreen mode Exit fullscreen mode

We've taken what is complicated (getting the request) and turned it into a feature of our custom render wrapper so that developers can write tests more easily.

This can be true for a lot of things. redux, session handling, theming, etc... If something is very common in your app there is no rule that should keep you from extending your testing utilities.