2017-03-13 16 views
1

Redux ReactアプリケーションをテストするときにJestが非常に役立つことがわかりました。しかし、非同期アクションの作成者をテストする方法の例はたくさんありますが、非同期コンポーネントのスナップショットを作成する方法は実際には分かりません。JestとReduxを使用した非同期コンポーネントのスナップショット

私がしたいのは、hovered link example from Facebook's own tutorialに似ています。彼らは、props関数onMouseEnter()を呼び出し、続いてスナップショットを取得します。 onMouseEnter()がRedux Thunkで作成された非同期アクションをディスパッチする場合、簡単な方法がありますか?

これは私のサンクがアキシャスを使用するように見える方法です。

// test-api.js 

    export function getLinkInfo() { 
    return function(dispatch) { 
    return axios.get('/api/get-link-info') 
    .then(response => { 
     dispatch(getLinkInfoSuccess(response.data)); 
     return response; 
    }); 
    }; 
} 

ここに自分のリンクコンポーネントがあります。

import React from 'react'; 
import { connect } from 'react-redux'; 
import * as api from '../../api/test-api'; 

class Link extends React.Component { 
    render() { 
    return (
     <a href='#' onMouseEnter={this.props.getLinkInfo}> 
     Hover me 
     </a> 
     <div>{this.props.linkInfo}</div> 
    ); 
    } 
} 

const mapDispatchToProps = function(dispatch) { 
    return { 
    getLinkInfo: function() { 
     dispatch(api.getLinkInfo()); 
    } 
    } 
} 

const mapStateToProps = function(store) { 
    return { 
    linkInfo: store.getIn(['testState', 'linkInfo'], "") 
    }; 
}; 

export default connect(mapStateToProps, mapDispatchToProps)(Link); 

最後にテストファイル。

import React from 'react'; 
import Link from '../link'; 
import renderer from 'react-test-renderer'; 

test('Link changes linkInfo when hovered',() => { 
    const component = renderer.create(
    <Link></Link> 
); 
    let tree = component.toJSON(); 
    expect(tree).toMatchSnapshot(); 

    // manually trigger the callback 
    tree.props.onMouseEnter(); 
    // re-rendering 
    tree = component.toJSON(); 
    expect(tree).toMatchSnapshot(); 
}); 

答えて

1

問題は、非同期のものをテストしたいとき冗談はそれについて知っていて、それを待つ、またはasyncを使用できるようにあなたは、どちらかがテストからそれを返すために、あなたのテストで約束のインスタンスを必要とするということです自己のテストの中で待つ。(docs)。 jest.mock( 'パス//API /に '/ API /に/パス' から

インポート{getLinkInfo}:あなたが何ができるか

は、あなたのテストの内部のAPIを模擬することです」、()=>({ getLinkInfo:jest.fn() }))

これはgetLinkInfoためにスパイを有するオブジェクトにモジュールを上書きします。次に、テストでスパイの実際の実装を設定できるように、モジュールをインポートします。

test('Link changes linkInfo when hovered',() = > { 
    //create a new promise that can be returned from your test 
    const p = new Promise((resolve) = > { 
    //set the spy to make the request and resolve the promise 
    getInfo.mockImplementation(function (dispatch) { 
     return axios.get('/api/get-link-info') 
     .then(response = > { 
      dispatch(getLinkInfoSuccess(response.data)); 
      resolve(response); 
     }); 
    };) 
    };) 
    const component = renderer.create(
    <Link></Link> 
); 
    let tree = component.toJSON(); 
    expect(tree) 
    .toMatchSnapshot(); 
    // manually trigger the callback 
    tree.props.onMouseEnter(); 
    return p.then(() = > { 
    tree = component.toJSON(); 
    expect(tree) 
     .toMatchSnapshot() 
    }) 
}); 

これはあなたの実際の問題を解決するかもしれないが、私はあなたの本当のAPIを呼び出して、このようなテストを実行しないことをお勧めするだけでなく、要求、それを自己のモックでしょう。まず、テストははるかに高速になり、実行中のバックエンドに依存しません。

Reactコンポーネントをユニットとしてテストしたいので、getLinkInfoを呼び出してから何が起こったかは気にしません。これらはユニットテストの詳細ですgetLinkInfoです。あなたのコンポーネントが知っていることはすべて、getLinkInfoがコールバックで渡され、このコールバックが時々呼び出されることです。それが呼び出され、その間に何が起こったのかは、コンポーネントの責任の一部ではありません。このようなテストについて考えるなら、最も簡単な解決策はコールバックをすぐに呼び出すことです。

test('Link changes linkInfo when hovered',() = > { 
    getInfo.mockImplementation(function (dispatch) { 
    dispatch({ 
     some: 'Data' 
    }); 
    };) 
    const component = renderer.create(
    <Link></Link> 
); 
    let tree = component.toJSON(); 
    expect(tree) 
    .toMatchSnapshot(); 
    // manually trigger the callback 
    tree.props.onMouseEnter(); 
    tree = component.toJSON(); 
    expect(tree).toMatchSnapshot() 
}); 
+0

ありがとうございました。バックエンドとReduxフロー全体を単一のテストでテストする理由は、バックグラウンドで実行できる自動ブラウザGUIテストに代わるものでした。だから私は基本的にJestでコンポーネントが再レンダリングするのを待つことができる待機メソッドが必要になります。それは本当に悪い考えですか? –

+1

この種のテストを行うのは悪い考えではありませんが、セレンやサイプレスのような統合テストのためのより良いツールがあります。 –

0

チュートリアルでは、ステートフルなコンポーネントがあります。それはそのような「体操」を行う必要があります。純粋な、ステートレスなコンポーネントの場合

、あなたが持っているように、1べき唯一のテスト二つのこと:それはpropsの任意の組み合わせで正しくレンダリングする

  1. 正しいハンドラが一定で呼び出されていることイベント。

ただし、connectが生成するHOCのみをエクスポートします。両方をエクスポートすることでこれを解決できます(またmapDispatchToPropsmapStateToProps)。あるいは、connectを嘲笑して、元のコンポーネントを返すようにします。

import … 

export class Link extends React.Component { 
    … 
} 

export const mapDispatchToProps = … 

export const mapStateToProps = … 

export default connect(mapStateToProps, mapDispatchToProps)(Link); 

とテスト:

import … 
import { Link, mapDispatchToProps, mapStateToProps } from './Link' 

test('renders correctly',() => { 
    const tree = renderer.create(
    <Link linkInfo="Link info" /> 
).toJSON() 

    expect(tree).toMatchSnapshot() 
}) 

test('calls getLinkInfo',() => { 
    const getLinkInfo = jest.fn() 

    const tree = renderer.create(
    <Link getLinkInfo={getLinkInfo} /> 
) 

    tree.props.onMouseEnter() 

    expect(getLinkInfo).toHaveBeenCalled() 
}) 

test('mapDispatchToProps',() => …) 
test('mapStateToProps',() => …) 

これは純粋なコンポーネントの完全テストです

ファイルは次のようになります。


質問の2番目の部分は、非同期アクション作成者をテストすることです。トリッキーな部分はaxiosです。それはどこから来たのですか?私はあなたが上にそれをインポートすると仮定します。だからあなたはそれを嘲笑しなければならないだろう。

extraArgumentには、還元サンクに渡すことのできるものはあまりありません。これは、純粋な依存性注入として機能することができます。これにより、アクション作成者は簡単にテストできます。このような

用途:

const store = createStore(
    reducer, 
    applyMiddleware(thunk.withExtraArgument({ axios })) 
) 

次に、この依存関係(またはそれ以上必要な場合は)サンクへの3番目の引数として渡されます。

export function getLinkInfo() { 
    return function(dispatch, getState, { axios }) { 
    return axios.get('/api/get-link-info') 
    .then(response => { 
     dispatch(getLinkInfoSuccess(response.data)); 
     return response; 
    }); 
    }; 
} 

今クール来ます。非同期アクションクリエイターのテスト:

import * as actions from './actions' 

describe('getLinkInfo',() => { 
    const action = actions. getLinkInfo() 

    const dispatch = jest.fn() 
    const getState =() => { … } 
    const axios = { 
    get: jest.fn(() => Promise.resolve({ 
     data: {} 
    })) 
    } 

    beforeEach(() => { 
    deps.axios.get.mockClear() 
    }) 

    test('fetches info from the server',() => { 
    action(dispatch, getState, { axios }) 

    expect(axios.get).toHaveBeenCalledTimes(1) 
    expect(axios.get.mock.calls).toMatchSnapshot() 
    }) 

}) 

P.S.私はここにこれらのいくつかのより良いJestテストパターンを示します: https://github.com/robinpokorny/jest-example-hannoverjs

+0

素晴らしい例です。現在、私は自分でこのアプリケーションを開発しており、あまり時間がかからず、コンポーネント、アクションクリエーター、レデューサーの書き込みテストには時間がかかります(少なくともWET以上)。しかし、代わりにSelenium Webdriverを使うべきでしょう。私にとって重要なのは、大きな範囲で回帰テストを行うことです。 –

関連する問題