Are you stuck with Proxyquire or Sinon?
Before we moved to Jest, we used Karma with Mocha/Chai. These come with no built-in mocking capabilities (as far as I’m aware). If you’re a C# developer, you know all about the different Mock frameworks (e.g. Moq), and you want to be able to do the same with your TypeScript, right? At least I do. I want to try my best to follow the SOLID principles (I’ll write about those later in the year).
So, because these testing frameworks come with no mocks, we had to use things like Sinon or Proxyquire. It’s doable. It really is. But why do you need these extra frameworks and all this extra code when there is already a test runner/framework that has it all built in. And… and it works perfectly with React because it’s built by the same guys (Facebook).
Enter Jest
Jest is an excellent testing framework with pretty good out of the box functionality. Firstly, it will parallelise your tests, so they’ll run faster. It only takes a couple of minutes to run hundreds/thousands of tests. I think when we implemented it, it was about half the time. It has excellent mocking built in. This is awesome for TypeScript because you can mock entire imports. There’s just one caveat; make sure the mock is before the component that imports the import you want to mock.
Enzyme
Before I continue about mocking (in tests), I should talk about Enzyme (from Airbnb). Enzyme is a godsend. It makes testing React code so much better. But, you have to be careful. We try to use shallow rendering rather than a full DOM render (mount). Why? Because it’s much faster as you don’t render everything. Also because you shouldn’t need to test child components within the parent component. I mean, you wouldn’t do that in C#, so why would you do that with Enzyme?
Below is an example of how you can test a component. Even without seeing the actual component’s code, you can get an idea of what the component looks like. It has a class named .my-class and that has text inside of the props name. We can also see that it probably has a wrapper div with a class of .my-class-wrapper.
import * as React from 'react'; import { shallow } from 'enzyme'; import { MyComponent, MyComponentProps } from './MyComponent'; describe('<MyComponent />', () => { const initialProps: MyComponentProps = { name: 'John', }; test('Should display name correctly', () => { const props = { ...initialProps, }; const myComponent = shallow(<MyComponent {...props} />); expect(myComponent).toMatchSnapshot(); expect(myComponent.find('.my-class').at(0).text()).toBe(props.name); expect(myComponent.prop('className')).toBe('my-class-wrapper'); }); });
Mocking
Sometimes you have complicated components with dependencies that you don’t want to worry about. Maybe, for example, you decide to use Google Maps, which has an external dependency in your SPA. It does a network call and fails every time on your build server which doesn’t have internet access. You might have something else crazy. Who knows? Anyway, the point is that maybe you don’t want that loading during your tests. You can mock your API calls, or anything else.
import * as React from 'react'; import { shallow } from 'enzyme'; import { IMyApi } from './api'; const mockOnLoadFn = jest.fn(); const MyApi = jest.fn<IMyApi>((): IMyApi => ({ getMyName: mockOnLoadFn, })); jest.mock('./api', () => ({ MyApi, })); import { MyComponent, MyComponentProps } from './MyComponent'; describe('<MyComponent />', () => { const initialProps: MyComponentProps = { name: 'John', }; test('Should display name correctly', () => { const props = { ...initialProps, }; const myComponent = shallow(<MyComponent {...props} />); myComponent.instance().componentDidMount(); expect(mockOnLoadFn).toHaveBeenCalledTimes(1); }); });
You can tell from my test above, that probably during componentDidMount, getMyName on the IMyApi is called once. This won’t call out to the actual API. It’s nicely mocked and can easily be tested. I know that calling into the instance of the component is not the best thing, but if you don’t want to mount a component, this is the better way to do it.
You can even return a value from the mock. It’s probably better to look at the Jest documentation rather than me trying to blabber about this. UNLESS… unless I get some more requests about mocking in tests etc.
Summary
Use Jest to test your React code. Use Enzyme in conjunction with React. Mocking/Stubbing is good where possible.
Reposted on Medium.
Leave a Reply