We automate testing of redux selectors in the application.

    In this article, I would like to share my ideas on how to automate writing unit tests in react / redux applications. These ideas were born in one of the discussions with colleagues, in the process of writing tests, and it seems to me that the proposed solution has the right to life.

    About the problem


    I would like to omit thinking about the need for unit tests and get right down to business. How are we testing selectors now?

    A redux selector is a function that takes an application's state (store) and returns the result. Even if the selector is created using reselect createSelector () and combines several selectors, it also takes the store as input.

    Accordingly, to test the selector, for good, you need to pass into it the full stop. Of course, creating a mock store can eliminate unnecessary areas, but if we test a complex selector that combines other selectors from different parts of the store, we will have to recreate a complete or almost complete state. And so for each case.

    Here you can go in several ways:

    • Recreate the state of the application in the test environment and unload the state, using, for example, the redux devtools extension;
    • Just create an object, what is called, pens. If the application is large and its state contains a lot of “branches”, this can be quite a complicated and painstaking process.

    The idea of ​​automating the process


    Imagine that you have middleware, which, among other things, knows about all the selectors of the application. After each action, such middleware can calculate all selectors and prepare a test case consisting of:

    • Event occurred (action);
    • Application state (store);
    • The result of executing all the selectors with this state;

    Thus, for each action, we will have a set of data: the state of the application for transfer to the selectors and the expected results - in fact, to compare.

    Those. we have data sets from a real application, which took a minimum of time to prepare.

    It remains only to learn how to save this data and somehow automatically perform selectors and compare the result, but this task is simple, technical, and I have solved for you. In this chapter, I just wanted to convey the idea.

    How it works?


    If the whole idea is clear (and it seems adequate to you :)), I suggest to start implementation. To begin, we need the browser extension Redux CheckState .
    This extension gets all the actions of your application, executes selectors and saves test cases. In the end, there you click on the button and download the file with the resulting test cases.

    It looks like this:

    Redux CheckState Screenshot

    In order for the extension to receive data on the events that are taking place and to be able to execute the forceps, you need to make small manipulations with the project.

    Step 1. Export selectors


    At the root of the project, you need to create the checkState.config.js file and export from it all the selectors that you would like to test. In my test project, it looks like this:

    export {
        selectCategories,
        selectActiveCategory,
        selectIsCategoryActive,
        selectActiveCategoryId,
    } from"./state/category";
    export {
        selectPopup,
        selectPopupType,
        selectIsPopupOpen,
    } from"./state/popup";
    export {
        selectTasks,
        selectActiveTasks,
        selectActiveDoneTasks,
        selectActiveInProgressTasks,
    } from"./state/task";

    See an example on github .

    Step 2. Implementation of middleware


    Now you need to add middleware, which will transfer all actions and other data to the extension.

    The code is extremely simple:

    import * as selectors from"./checkState.config";
    const checkStateMiddleware = (options = {}) => {
        returnwindow && window["__checkStoreExtension__"] ? window["__checkStoreExtension__"](options) :
            store => next => action => next(action);
    };

    In my test application, you can also look at the implementation options for typescript.

    Everything, the writing of the code is complete. Now we start the application, open the extension and start using the application as a user. You need to make as many action games as possible. Every perfect action you will see in the extension. You can also click on any action and see the results of the execution of selectors.

    When all actions are perfect - just download the file and put it in the project. Now you just run the tests. It's still easier.

    Running tests


    To run the tests, I prepared the CLI tool. Install the package:
    npm i check-state -g

    After that, in the project folder, execute the command:
    check-state start

    Check state CLI will find the test cases file generated by the browser extension, find and compile the exported selectors (while javascript and typescript are supported).
    After that, all the test cases will be successively passed, each selector will be executed with the application state from the test case and the calculated value will be compared with the expected one (also from the test case). If there is no difference, we will see a green line, if there is a red line, with information that will help diagnose the problem:

    • The name of the selector that returned the incorrect result;
    • Expected Result;
    • Current result;
    • Application state cast from test case.

    Check state CLI tool screenshot
    An example of a "fallen" test.

    So that you can experiment with the tool - I have prepared a test application in which there are several selectors and have already implemented the Check state: example application

    Conclusion


    I hope you liked the idea of ​​automating auto-test writing, and maybe you will implement this approach in your project :)

    If you are interested in the technical implementation of the tools:


    I will welcome ideas and comments :)

    Also popular now: