react-test-tube

    1.0.1 • Public • Published

    React Test Tube

    React Test Tube is a lightweight tool to perform A/B and multi-variate split tests in your React code.

    Installation

    Install React Test Tube using NPM.

    $ npm i --save react-test-tube

    Usage

    To perform a simple A/B split test of two components in your React application create an <Experiment /> containing two or more <Variant /> components. By default the winning <Variant /> is chosen at random. You can customise this by providing a reducer function.

    <Experiment name="my_experiment">
        <Variant name="my_variant_a">
            <ComponentA />
        </Variant>
        <Variant name="my_variant_b">
            <ComponentB />
        </Variant>
    </Experiment>

    Experiment

    <Experiment
        name="my_experiment"
        reducer={Random()}
        cache={Cache()}
        onParticipation={participationCallback}
    >
        ...
    </Experiment>
    Prop Description Default Required
    name The name of the experiment. - Yes
    reducer An instance of a reducer function. Random No
    cache An instance of a cache function. Cache No
    onParticipation A function that is called when a variant is chosen. - No

    Variant

    <Variant name="my_variant_a">
        ...
    </Variant>
    Prop Description Default Required
    name The name of the variant. - Yes

    Reducers

    A reducer is a function that takes an array of <Variant /> components as an argument and returns a single winning <Variant /> which is to be rendered. Several reducer functions are included out of the box or you can create your own reducer function by providing it to the reducer prop of an <Experiment />.

    Note: The built-in reducer functions are in fact factory functions whose return value is the reducer function itself, allowing for configuration of the reducer. This is why, when specifying a built-in reducer, we must use reducer={Random()} and not reducer={Random}. See "Creating a custom Reducer" for more details.

    Random

    The Random reducer selects a winning <Variant /> at random. It is the default reducer and does not need specifying.

    import { Experiment, Variant, Random } from 'react-test-tube';
    
    const App = () => (
        <Experiment name="my_experiment" reducer={Random()}>
            <Variant name="variant_a">
                <h1>Variant A</h1>
            </Variant>
            <Variant name="variant_b">
                <h1>Variant B</h1>
            </Variant>
        </Experiment>
    );

    Modulo

    The Modulo reducer selects a winning <Variant /> based on a number provided in the configuration, such as a user ID. For an <Experiment /> with three <Variant /> components, when providing the number 2 as the only argument to the reducer, the calculation in effect is 2%3. The result is shifted by -1 in order to show the most logical <Variant />. Therefore, 2%3 would return the second <Variant /> at index 1 rather than the third (2%3 = 2).

    import {Experiment, Variant, Modulo } from 'react-test-tube';
    
    const App = ({ userId }) => (
        <Experiment name="my_experiment" reducer={Modulo(userId)}>
            <Variant name="variant_a">
                <h1>Variant A</h1>
            </Variant>
            <Variant name="variant_b">
                <h1>Variant B</h1>
            </Variant>
        </Experiment>
    );
    Argument Description Default Required
    id An integer to use to determine the modulos, such as a user ID. - Yes

    Query String

    The QueryString reducer selects a winning <Variant /> based on paramters provided in the query string of the URL. By default the parameters are exp (to specify the experiment) and var (to specify the variant). The reducer can be customised via its configuration to look at different parameters in the query string. Additionally, a fallbackReducer can be provided where the required parameters are not present in the URL. By default this is not provided and will return the first <Variant />.

    import { Experiment, Variant, QueryString, Random } from 'react-test-tube';
    
    const App = () => (
        <Experiment name="my_experiment" reducer={QueryString('exp', 'var', Random())}>
            <Variant name="variant_a">
                <h1>Variant A</h1>
            </Variant>
            <Variant name="variant_b">
                <h1>Variant B</h1>
            </Variant>
        </Experiment>
    );
    Argument Description Default Required
    experiment The query string parameter that provides the ID of the experiment exp No
    variant The query string parameter that provides the ID of the winning variant var No
    fallbackReducer A reducer function that will be called if the query string does not contain the required parameters. - No

    Google Optimize

    The GoogleOptimize reducer provides an integration with Google Optimize experiments, providing the advantages of managing and analysing your experiments from the Google Optimize tool whilst creating your variants as React components.

    Required: Google Optimize is required to be installed on the page. Follow the instructions to install Google Optimize on your website.

    Credit: The GoogleOptimize reducer uses the @react-hook/google-optimize library. If you do not require the additional functionality provided by react-test-tube then I recommend that you check out this library.

    import { Experiment, Variant, GoogleOptimize } from 'react-test-tube';
    import MyLoadingComponent from 'MyLoadingComponent';
    
    const App = () => (
        <Experiment
            name="my_experiment"
            reducer={GoogleOptimize(
                'GOOGLE_OPTIMIZE_EXPERIMENT_ID',
                <MyLoadingComponent />,
                6000
            )}
        >
            <Variant name="variant_a">
                <h1>Variant A</h1>
            </Variant>
            <Variant name="variant_b">
                <h1>Variant B</h1>
            </Variant>
        </Experiment>
    );
    Argument Description Default Required
    experimentId The ID of the experiment as provided in the Google Optimize interface. - Yes
    loadingState A component that will be rendered whilst the winning <Variant /> is chosen. null No
    timeout The time in ms in which a winning <Variant /> must be chosen before defaulting to show the first. 3000 No

    Creating a custom Reducer

    A reducer is a function that takes an array of <Variant /> components as an argument and returns a single winning <Variant /> which is to be rendered. You can therefore provide any function that follows that convention.

    const AlwaysChooseFirstVariant = variants => variants[0];
    
    <Experiment name="my_experiment" reducer={AlwaysChooseFirstVariant}>
        ...
    </Experiment>

    As a reducer is just a function, you can also use a factory function to provide additional configuration. This is a pattern that is used for all built-in reducer functions.

    /* This is just an example. I recommend coding more defensively than this! */
    const HardCodeWinningVariant = index => variants => variants[index - 1];
    
    <Experiment name="my_experiment" reducer={HardCodeWinningVariant(2)}>
        ...
    </Experiment>

    Caching

    When a winning <Variant /> is chosen it is assumed that you will want your user to see the same <Variant /> on subsequent sessions, and not receive a different <Variant /> if they reload the page or navigate away and return later. The built-in Cache function caches the winning <Variant /> in Local Storage and returns it from the cache on future visits, bypassing the reducer function.

    The built-in Cache is provided by default. You do not need to specify it when creating an <Experiment />. You can provide a custom cache function by setting the cache property.

    A cache function is similar to a reducer. It is provided with the name of the experiment (to help avoid naming collisions with other cached experiments), the array of <Variant /> components, and a reducer function to run in the case of a miss (this reducer function is passed on from the <Experiment />).

    const SessionStorageCache = () => (experiment, variants, reducer) => {
        const cacheKey = `exp_${experiment}_variant`;
        const cachedVariantName = window.sessionStorage.getItem(cacheKey);
        ...
        return chosenVariant;
    };
    
    <Experiment name="my_experiment" cache={SessionStorageCache()}>
        ...
    </Experiment>
    Argument Description Default Required
    experiment The name of the experiment. Helps to avoid naming collisions. - Yes
    variants An array of <Variant /> components. - Yes
    reducer A reducer function to call if there is a cache miss. - Yes

    Participation Callback

    When a winning <Variant /> is chosen you can access the name of both the <Experiment /> and the winning <Variant /> via the onParticipation callback.

    const myOnParticipationCallback = (experimentName, variantName) => {
        console.log(`I chose the ${variantName} variant for the ${experimentName} experiment.`);    
    };
    
    <Experiment name="my_experiment" onParticipation={myOnParticipationCallback}>
        ...
    </Experiment>

    Contributing

    If you find a bug or would like to contribute, please raise an issue and create a pull request on GitHub. Thanks!

    Install

    npm i react-test-tube

    DownloadsWeekly Downloads

    62

    Version

    1.0.1

    License

    MIT

    Unpacked Size

    22.4 kB

    Total Files

    19

    Last publish

    Collaborators

    • avatar