TL:DR I really like my workflow of Jasmine, Karma, Enzyme integration/unit tests for React/Redux testing and you should consider it too for projects longer than a weekend hack.
Over the last month, I’ve been researching unit tests as my project grows and becomes harder to maintain/debug. Before then, I’ve just been doing manual testing and relying on my ‘awesome’ javascript skillz to not break the code
This has been my third foray into unit testing over the last decade after the first two failed attempts. I’ve been a believer in end-to-end happy path automated regression tests, just not unit tests.
In the last week, I’ve written 1k lines of test code, increasing my codebase by 50% from 2k to 3k LoC.
The setup:
- Karma test runner with webpack allows for writing tests in ES6
- Enzyme gives me
shallow(<Component {...props}/>)
rendering which means I test each component separately. - Jasmine takes care of everything Mocha, Chai, Sinon gives me
- Everything in the
tests
folder and watched by Karma on file change - React pure components is very easy to test. All inputs go through the props, there are no other dependencies, and I use Enzyme to look up the html output.
- Redux means I don’t use any react component state and I can test close to 100% of my business logic with very little use of test doubles, spies, mocks, stubs etc.
What it gives me:
- I currently have 86 tests over 2 pages of my app. These run in 0.15 seconds from the moment I save a file change.
- Most changes I make is picked up by a test breakage.
- These breakages allows me to quickly find the code that’s broken and from the name of the test that broke, I almost always know what broke and why. This has allowed me to really speed up my development cycle because I used to have to go by whatever threw a javascript exception and then start debugging in chrome dev console. This was very time consuming.
- It’s really helped me to clean up my functions, components, modules and minimise API surface between modules. This is because whenever I write a test, it becomes obvious when parameters to functions should not be necessary or even functions themselves so it’s helped me clean up my design alot!
- Fuzzy happy feelings when I see my tests pass and the confidence to go crazy on my refactoring. This was a major reason for writing the tests in the first place. Not the fuzzy feelings, the ability to refactor with confidence.
- Every bug I find, I first write a test for it, prove it fails, then implement the feature and see the test pass. Then I do manual testing afterwards just for self gratification as it almost always passes.
I work on a 80s game remake.
My tests can be found in the tests directory.
Example of a in-depth stateless component test.
Example of game logic test.
I haven’t started testing async actions from Meteor.calls or server-side interactions, that will be another stepping stone to TTD (Total Test Domination)!
My method for testing isn’t pure unit test. It started off that way but I found that it was too artificial to test my reducers, actions and mapState/Dispatch separately so what I do is setup a store, dispatch actions until I get the state that I need for the component under question and then test the component. The examples above show this. In practice, it it a bit more integration testing but I’d argue that for a React/Redux app, your store/state/component is one integrated unit and should be tested as such.
To make sure that the actions and reducers work as expected, I test these two together without components so any logic issues in the actions/reducers will be picked up by both component testing and action/reducer testing.
I’d be happy to hear how others are testing their code, whether people prefer end-to-end tests and if anyone actually had the time to look at mine, any suggestions for improvement would be well received! After all, I’ve only been doing this for a month.