Testing Principles - coursehero/ch-react-workshop GitHub Wiki

Why bother writing tests?

  • If you don't write tests someone will eventually write code that breaks your feature. That someone could be you.
  • Tests are a formal proof that your code works and will continue to work in future. Therefore most code reviews should include automated tests (exceptions are simple text or style changes, as long as your tests are resilient).
  • Since tests are meant to prevent future failures, any time you find a bug in production you should ask how could have it been prevented (hint: you might be missing test coverage).

Testing Principles

  • As a great rule of thumb to stick to when writing any kind of front end tests, regardless of whether it’s using RTL or Cypress, is to ensure that your tests resemble the way your software is used as closely as possible in order to give you more confidence that you’re actually testing the use cases that your users will be running into.

  • Stay away from testing the implementation details of your components and functions. There are some edge cases where you’ll have to violate this rule, but those cases shouldn’t be too common or else it will most likely lead to brittle tests that yield false positives and/or false negatives.

  • Write your tests with two users in mind only: the end user that will interact with your components, and the developer who will be making use of those components. Read on for some examples of this, and avoid the test user.

  • Focus on testing the use cases your users care about. Test coverage numbers are a useful metric for spotting which sections of your codebase may be in need of more tests and to encourage your team to write more tests. But they absolutely do not guarantee that your tests are solid and actually cover the use cases that your users will be interacting with in your UIs. In fact, you could have a really high test coverage number backed by tests that are brittle, break easily, are hard to maintain, test implementation details, and all kinds of hellish things – this is especially true for front end testing, where a lot of times you have to account for edge cases and situations that you wouldn’t have to think about on the server. Check out this Google article for some other thoughts around this subject.

Checklists

Best Practices

  • Write a manual testing plan before you start working on a feature. How would somebody unfamiliar with the feature test it?
  • Go through the before and after checklists below.
  • Write a failing test first. Make it pass by implementing your feature.
  • Think about accessibility and mobile first.
  • Keep your code reviews short but do add screenshots/screencasts of Cypress tests showcasing your feature. This will help reviewers understand what you changed.
  • Organize a snack-themed manual testing party before releasing a bigger/complicated feature/AB test.

Before writing tests

  • Small (corner cases) vs medium (testing the main happy flow)
  • User type (logged out, basic, premium, special features...)
  • AB test buckets
  • When do you want the test to fail? How exactly should it fail?
  • Who owns the resource you're relying on (can it be taken down/DMCA'd/disappear?)
  • Can you use accessible queries as much as possible? Use the RTL PLayground to debug your HTML (there's even a browser plugin).
  • Understand how do you make tests resilient to change (https://kentcdodds.com/blog/making-your-ui-tests-resilient-to-change)

As you develop your code

  • Think about testing the component and where you can place query text or labels.
  • If you’re using the same text in multiple places consider making it a constant to share between components and tests.
  • If you can write an accessible query using the constant you probably don’t need aria-label or testid.

Example: create a constant with CTA text to be shared w/the component and tests. Then use .findBy* or .queryBy* to find the CTA or item you need.

💡 Tip: use the Testing Playground to help debug your HTML for accessibility and to select the best query.

After writing tests

  • Are there any accessibility violations?
  • Have you considered different breakpoints/viewports?
  • Have you checked test coverages? For small tests run npx jest --coverage
  • Does the automated test match the manual testing plan? What is missing?

How to write tests

Types of tests

One way to classify tests is to follow the famous Google blog:

Generally we have a preference for medium tests since these tests allow us to get a good test coverage with relatively few lines of code. Small tests are great for testing pure functions and various corner cases. Large tests unlike medium tests do not stub (or mock) API responses but you need to worry about polluting the database, network timeouts etc.

At Course Hero (and for this workshop), we have settled on three libraries/frameworks for writing and running automated tests for our React apps. They are: Jest, React Testing Library (RTL), and Cypress. Together, they cover Small, Medium, and Large test cases, or as they are commonly known in the front end community: Unit, Integration, and E2E.

It’s worth nothing that you may run into cases where you’re wondering whether you should use RTL or Cypress to write your tests in. There are clear differences between these tools, such as speed, ease of use, simplicity, and being able to visually see your tests run, and we encourage you to read their documentation and share any interesting learnings with your team. Note that, generally speaking, Cypress is a testing tool known for its E2E/integration capabilities (although it’s been expanding to cover unit tests as well), and some of the key features they tout are specific to End-to-End testing. In short, React Testing Library and Cypress are meant to be used together (they actually integrate nicely with each other) and one is not meant to replace the other.

Other related topics

Next Steps

⚠️ **GitHub.com Fallback** ⚠️