FE Software Testing: Griff’s Opinion
As a test-driven developer, I’ve been asked for my take on front-end (FE) testing. Here’s my perspective:
Brief Overview of TDD
Test-Driven Development (TDD) means you change the tests first when you change software. You don’t write production code unless there’s a failing test, and you don’t refactor code unless it’s covered by tests and the tests are green. We follow the cycle of red/green/refactor and aim to avoid over programming. While 100% test coverage is ideal, it’s not always achievable, especially with AI-generated code assistance and TypeScript. However, the goal remains to avoid writing code without a corresponding test and to avoid writing tests that are guaranteed to pass. Although a TDD developer may generate code that isn’t 100% covered, the tests will be more reliable and the coverage will be high.
TDD offers benefits beyond test coverage, including more modular code and emergent design, which helps avoid over-engineering and unnecessary complexities.
Griff’s TDD Experience
I learned about testing before I could write meaningful tests. Initially, the idea of testing seemed good, but the application was often “clear as mud.” The examples were contrived and didn’t demonstrate how to establish test coverage on written code or interact with databases, file systems, etc. Refactoring code to be more testable is a separate skill that requires testing knowledge, and most developers start there.
I’ve had bad experiences working on teams that approached testing poorly, such as testing after the fact, which resulted in untestable code and slow test suites running essentially end-to-end (e2e) tests.
Eventually, I joined an Angular project at a company that practiced extreme programming. I didn’t know Angular or TypeScript and barely knew ES6, but I quickly became productive and learned both the framework and language effortlessly. Large refactors in that project showcased the benefits of testing. Any other project I worked on there required tests, even inherited ones. This experience provided quality training on writing tests.
Testing the FE
It’s clear that I favor tests and prefer TDD. For the FE, my opinion is that logic should be tested, but rendering React components with Jest for UI functionality is a waste of time and creates unnecessary work. This may seem to contradict my belief in testing, but it doesn’t, as I’ll explain.
Code Organization
I believe in keeping React components as simple as possible, handling only rendering logic. Separating code concerns allows logic and setup to be handled elsewhere, making it easier to test those other logic areas as just code without needing a React component. This approach stems from my background in MVC frameworks and real separation of concerns.
How to Test FE
While I value coverage over FE components, I don’t think unit tests are beneficial for this purpose. In my experience, two approaches stand out: PatternLab and Storybook.
PatternLab
Initially, I used PatternLab with Timber and Twig in WordPress, allowing us to use JSON for data instead of the usual PHP and Twig templates for FE components/pages. Later I used it with things like the Symphony Framework for PHP. PatternLab provides a dashboard UI where you can see all components and author JSON files to send data into those components, changing their state. This modular approach lets you view every state of every component, including higher-level pages containing other components.
Storybook
Storybook is similar to PatternLab, offering a catalog browser and UI where you can toggle or enter changeable values. It includes features like toggle switches for booleans and name/value editors for data. This setup is excellent for React, allowing you to wrap things in contexts and author stub data in Storybook.
Conclusion
Ideally, FE components should focus on handling display logic only. I don’t think this logic needs to be tested with unit tests, as there’s a preference for UI component libraries. This approach emphasizes separation of concerns and eliminates the need for rendering and adding complexity to test suites solely for UI purposes.