Automated tests are the cornerstone of development. I want my tests to run as fast as possible and I want each failure to guide me directly towards a fix. Unit testing is a great solution for this; the minimal code execution encourages speed and the isolation makes each failure informative. React already encourages composition and encapsulation so it's a great fit for unit testing.
But, wait, unit testing in a dynamic language is unsafe because there are no contracts between units. For example, if you mock a dependent React component using outdated props, how would you know? The unit test would still pass. Perhaps because of this, functional testing seems quite popular for React apps and I totally get it. The only type of test you can trust in a dynamic language is a functional test!
And then your application grows. It grows and grows and each functional test compounds under the weight of all the common building block components you are repeatedly rendering. You begin to get drowsy. You make some coffee. You check Twitter. What were you even working on?
Slow tests are a bummer. They zap my motivation to prototype new concepts for a large, established application and I want them dead.
It doesn't have to be this way.
Shallow rendering is when you only render the topmost component in the tree of JSX returned by
You can still make assertions about nested components (like which props they get) but the nested render functions will not get executed.
You can even use shallow rendering to test wrapped components, such as Redux connected components.
In a very elegant way (thanks to JSX), shallow rendering is a type of mock dependency injection but it's fully automatic, meaning you won't be swimming in mock configuration when reading test code. Using mocked dependencies results in less code to execute -- i.e. faster tests -- and it separates the concern of each test. A bug in a common building block component won't cause a cascade of failing tests. Your test failures will be concise and informative.
OK, so, what happens when you change the props accepted by a dependent component? The TypeScript compiler will instantly point to all JSX that sets outdated props. Nice! If you have a fancy editor, you may even be able to fix the JSX automatically.
Static analysis of component props is the key ingredient you can combine with shallow rendering to cook up a fast, effective, and highly scalable test suite. Remember how I said React encourages composition and encapsulation? This means you can trust that a component will do the right thing as long as it receives the correct props.
Again, I totally get the dangers of shallow rendering on its own without static anaylsis.
Wait, are we talking
public static void main() level of typing?
It infers a lot of types automatically and does duck typing.
But, still, is it worth the overhead?
This is a fair question and The TypeScript Tax makes a case that it's not worth it.
The article doesn't dispute the benefits of static analysis but it claims
you can achieve them just the same through linting.
I want to believe this but I'm not sure it's true. For example, I have seen React PropType linters get confused by ES6 destructuring. There are several variants of this bug still open today; the introspection abilities of a linter are limited. I'd be interested to read a follow-up article explaining how you can use linters to achieve parity with static typing. TypeScript is truly its own programming language so it stands a much better chance at introspection.
For example, it doesn't enforce function arguments whatsoever
and doesn't help you work with
false values (the source of many bugs).
In TypeScript, it's a lot harder to make simple mistakes.