sinon
とenzyme
を使用して次のコンポーネントをテストしたいと思います。
_// Apple.js
class Apple extends Component {
componentDidMount = () => {
this.props.start();
Api.get()
.then(data => {
console.log(data); // THIS IS ALWAYS CALLED
this.props.end();
});
}
render () {
return (<div></div>);
}
}
_
_endApy.called
_をチェックするだけでは、常にfalseです。ただし、setTimeout
でラップすると合格になります。なぜconsole.log()
は常に呼び出されますが、_props.end
_は呼び出されないのですか?なぜsetTimeout
がそれを修正するのですか?これを行うためのより良い方法はありますか?
_// Apple.test.js
import sinon from 'sinon';
import { mount } from 'enzyme';
import Api from './Api';
import Apple from './Apple';
test('should call "end" if Api.get is successfull', t => {
t.plan(2);
sinon
.stub(Api, 'get')
.returns(Promise.resolve());
const startSpy = sinon.spy();
const endApy = sinon.spy();
mount(<Apple start={ startSpy } end={ endApy } />);
t.equal(startSpy.called, true); // ALWAYS PASSES
t.equal(endSpy.called, true); // ALWAYS FAILS
setTimeout(() => t.equal(endApy.called, true)); // ALWAYS PASSES
Api.get.restore();
});
_
Api.get
は非同期関数であり、promiseを返すため、テストで非同期呼び出しをエミュレートするには、resolves
ではなくreturns
関数を呼び出す必要があります。
指定された値に解決されるPromiseをスタブに返します。 Promiseを構築するとき、sinonはPromise.resolveメソッドを使用します。あなたは、Promiseを提供しない環境でポリフィルを提供する責任があります。
sinon
.stub(Api, 'get')
.resolves('ok');
console.log(data)
は、Promiseが解決するために常に発生します。解決するのは、テストが終了した直後です。これが、アサーションが失敗する理由です。
これをsetTimeoutでラップすることにより、ループ内に別のイベントを作成します。これにより、テストが終了する前にPromiseを解決できます。つまり、アサーションは合格になります。
これは、非同期コードを単体テストする場合のかなり一般的な問題です。多くの場合、アサーションをsetImmediate
でラップし、done
のコールバックからsetImmediate
を呼び出すことで解決されます。