2

これは、AirBnB の React テスト ライブラリであるEnzymeを使用して React コンポーネントの一部をリファクタリングしようとしているときに遭遇した興味深い問題です。

私の問題を説明する最良の方法は、例を使用することだと思います。

以下は、親コンポーネントから受け取った props に応じてメッセージを表示する小さな React コンポーネントです。

test.js:

import React from 'react';

function renderInnerSpan() {
    const {foo} = this.props;

    if (foo) {
        return <span>Foo is truthy!</span>;
    }

    return <span>Foo is falsy!</span>;
}

export default class extends React.Component {
    render() {
        return (
            <div>
                {renderInnerSpan.call(this)}
            </div>
        );
    }    
}

そして、これはこのコンポーネントのテスト スイートで、2 つのテストに合格しています。

test.spec.js:

import Test from '../../src/test';

import React from 'react';
import {shallow} from 'enzyme';
import {expect} from 'chai';

describe('Test Suite', () => {
    let renderedElement,
        expectedProps;

    function renderComponent() {
        const componentElement = React.createElement(Test, expectedProps);

        renderedElement = shallow(componentElement);
    }

    beforeEach(() => {
        expectedProps = {
            foo: true
        };

        renderComponent();
    });

    it('should display the correct message for truthy values', () => {
        const span = renderedElement.props().children;

        expect(span.props.children).to.equal('Foo is truthy!');
    });

    it('should display the correct message for falsy values', () => {
        expectedProps.foo = false;
        renderComponent();

        const span = renderedElement.props().children;

        expect(span.props.children).to.equal('Foo is falsy!');
    });
});

これは問題なく動作しますが、Test コンポーネントの現在の実装は、可能な限り効率的ではありません。を使用することで、関数が呼び出される.call(this)たびに新しい関数を作成しています。次のように、コンポーネントのコンストラクターでrender()正しいコンテキストをバインドすることで、これを回避できます。this

export default class extends React.Component {
    constructor(props) {
        super(props);

        renderInnerSpan = renderInnerSpan.bind(this);
    }

    render() {
        return (
            <div>
                {renderInnerSpan()}
            </div>
        );
    }
}

この変更の後、コンポーネントは引き続き意図したとおりに動作しますが、テストは失敗し始めます。

AssertionError: expected 'Foo is truthy!' to equal 'Foo is falsy!'
Expected :Foo is falsy!
Actual   :Foo is truthy!

コンストラクターにa を追加しconsole.log(props.foo)ました。これにより、コンストラクターが期待どおりに呼び出されていること、および受け取っている小道具が正しいことが確認されました。しかし、私は のconsole.log(foo)内部を追加しました。プロパティを明示的に に設定しrenderInnerSpanてコンポーネントを再レンダリングした後でも、値は常に true のように見えます。foofalse

バインドされるのは 1 回だけのように見えますがrenderInnerSpan、Enzyme はこれをテストごとに再利用しています。それで、何が得られますか?テストでコンポーネントを再作成していますが、期待する値でコンストラクターを呼び出しています。バインドされたrenderInnerSpan関数が古い値を使用し続けているのはなぜですか?

助けてくれてありがとう。

4

2 に答える 2