Redux: Tidak dapat merujuk wadah yang dibungkus dengan Penyedia atau dengan terhubung dengan Enzim

Dibuat pada 20 Mar 2016  ·  51Komentar  ·  Sumber: reduxjs/redux

Sepertinya saya tidak dapat merujuk apa pun yang dibungkus dengan <Provider> dan connect

// test
let component = shallow(<Provider store={store}><ContainerComponent /></Provider>);
component.find('#abc'); // returns null

let component = shallow(<Provider store={store}><div id="abc"></div></Provider>);
component.find('#abc'); // returns the div node

// ContainerComponent
const Component = ({...}) => (<div id="abc"></div>);
export default connect(..., ...)(Component);

Saya mengikuti: https://github.com/reactjs/redux/issues/1481 ke contoh di mana tes ditulis dengan enzim, namun, wadah tidak pernah diuji di dalamnya. Jadi saya tidak tahu apakah saya harus/dapat menguji wadah pintar?

@fshowalter Ada ide?

Komentar yang paling membantu

Meskipun tidak berhubungan langsung dengan Redux. Saya memecahkan ini dengan memanggil shallow() pada wadah lagi. Itu membuat komponen dalam dengan status toko diteruskan ke dalamnya.

Contoh:

import React from 'react';
import {expect} from 'chai';
import {shallow} from 'enzyme';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import events from 'events';

const mockStore = configureMockStore([ thunk ]);
const storeStateMock = {
  myReducer:{
    someState: 'ABC'
  }
};

let store;
let component;
describe('tests for MyContainerComponent', () => {
  beforeEach(() => {
    store = mockStore(storeStateMock);
    component = shallow(<MyContainerComponent store={store} />).shallow();
  });

  it('renders container', () => {
    expect(component.find('div.somediv')).to.have.length.of(1);
  });
  ...
});

Semoga ini membantu

Semua 51 komentar

Baik connect() maupun Provider bukan bagian dari perpustakaan ini. Lebih mudah untuk mendiskusikan dan melacak masalah seperti itu ketika diajukan di repositori yang sesuai sebagai gantinya: https://github.com/reactjs/react-redux.

Saya pikir itu benar-benar masuk akal. Karena Anda melakukan rendering dangkal, hanya komponen Penyedia yang akan dirender - ContainerComponent Anda hanya akan dibiarkan dalam bentuk keluaran objeknya atau apa pun yang dihasilkan oleh rendering dangkal.

Dua pemikiran di sini:

Pertama, perhatikan bahwa komponen pembungkus yang dihasilkan oleh connect() sebenarnya mencari props.store sebelum mencari context.store , jadi jika Anda benar-benar merasa perlu menguji komponen yang terhubung , Anda harus dapat merender <ConnectedComponent store={myTestStore} /> tanpa perlu khawatir tentang Penyedia atau konteks atau apa pun.

Pertanyaan kedua adalah apakah Anda benar-benar perlu khawatir tentang benar-benar menguji komponen yang terhubung sepenuhnya. Argumen yang saya lihat dibuat adalah bahwa jika Anda dapat menguji komponen "polos" Anda dengan alat peraga tertentu, dan Anda dapat menguji implementasi mapStateToProps Anda, Anda dapat dengan aman berasumsi bahwa react-redux akan menyatukannya dengan benar, dan tidak perlu benar-benar menguji versi yang terhubung itu sendiri.

@gaearon Anda benar, maaf. Tidak tahu apakah akan menaikkan ini dalam reaksi atau repo enzim.

@markerikson Alasan untuk menguji komponen pintar adalah untuk mapToDispatchProps di mana saya ingin memastikan operator yang tepat dipanggil oleh komponen yang dibungkus. Hanya meneruskan toko ke ConnectedComponent berarti saya tidak akan menguji pemetaan status atau fungsi panggilan balik pengiriman.

Saya akan lebih memikirkan ini dan mengangkat masalah di repo yang relevan jika saya membutuhkannya. Terima kasih untuk bantuannya.

@mordra : ketika Anda mengatakan "petugas operator yang tepat dipanggil", apakah maksud Anda sebenarnya "pembuat tindakan yang benar"?

Anda dapat melakukan sesuatu seperti ini (sintaks mungkin tidak aktif, tetapi Anda harus mendapatkan ide):

let actionSpy = sinon.spy();

let wrapper = shallow(<PlainComponent someActionProp={actionSpy} />);
wrapper.find(".triggerActionButton").simulate("click");
expect(actionSpy.calledOnce).to.be.true;
expect(actionSpy.calledWith({type : ACTION_I_WAS_EXPECTING)).to.be.true;

Dengan kata lain, render komponen biasa Anda, berikan mata-mata untuk properti yang seharusnya merupakan tindakan yang dikembalikan dari mapDispatch , dan picu perilaku apa pun yang diperlukan komponen untuk membuatnya memanggil tindakan tersebut.

Juga, per komentar saya sebelumnya: Anda harus dapat menguji mapStateToProps dan mapDispatchToProps Anda secara terpisah, dan merasa aman bahwa React-Redux akan memanggil mereka dengan tepat, tanpa harus mencoba menguji versi yang terhubung sendiri untuk memverifikasi bahwa itu terjadi.

@markerikson Saya tidak bisa melakukan apa yang Anda sarankan karena PlainComponent saya tidak tahu tentang tindakan atau pembuat tindakan. Saya tidak tahu apakah ini cara yang tepat untuk melakukannya, tetapi jika Anda melihat:
https://github.com/mordra/cotwmtor/blob/master/client/charCreation/charCreation.jsx
Anda akan melihat bahwa mapDispatchToProps saya berisi semua logika:

const mapDispatchToProps = (dispatch) => {
  return {
    onCompleted      : (player) => {
      Meteor.call('newGame', player, function (data) {
        console.log('new game return: ' + data);
      });
      dispatch(routeActions.push('/game'));
      dispatch({type: "INIT_GAME", map: generateAreas(), buildings: generateBuildings(dispatch)});
    },
    onCancelled      : () => dispatch(routeActions.push('/')),
    onChangeName     : input => dispatch(actions.changeName(input)),
    onChangeGender   : gender => dispatch(actions.setGender(gender)),
    onSetDifficulty  : lvl => dispatch(actions.setDifficulty(lvl)),
    onChangeAttribute: (attr, val) => dispatch(actions.setAttribute(attr, val))
  }
};

jadi jika saya memata-matai alat peraga yang diteruskan ke PlainComponent, saya tidak akan dapat menguji apakah tindakan yang tepat dipanggil, melainkan, hanya parameter yang diteruskan ke pembuat tindakan yang benar.
Saya kemudian perlu menguji komponen connect secara terpisah.

Ah. Itu membantu saya lebih mengerti.

Jadi dengan peringatan bahwa saya sebenarnya memiliki sedikit pengalaman praktis dalam tes menulis, beberapa pemikiran:

  • Apa yang sebenarnya Anda miliki adalah pembuat tindakan, mereka tidak didefinisikan secara terpisah karena Anda menginginkan akses ke dispatch . Itu tidak mudah diuji dengan sendirinya. Anda mungkin ingin dapat menguji perilaku mereka juga, dan untuk itu Anda ingin mendefinisikan mereka sebagai fungsi mereka sendiri di luar mapDispatch .
  • Semuanya terlihat seperti kandidat yang sangat baik untuk digunakan dengan redux-thunk, yang tentunya akan mempermudah untuk mendefinisikan fungsi-fungsi ini sendiri dan memungkinkan mereka mengakses dispatch .
  • PlainComponent _does_ Anda tahu tentang "tindakan", atau setidaknya panggilan balik dalam kasus ini, di mana Anda meneruskannya, dan di suatu tempat di komponen Anda, Anda menjalankan this.props.onSetDifficulty("HARD") atau sesuatu seperti itu. Ketika saya menyarankan untuk mengirimkan mata-mata untuk tindakan, ini adalah jenis hal yang saya sarankan untuk diganti. Jadi, jika Anda memasukkan mata-mata untuk onSetDifficulty , Anda dapat memverifikasi bahwa komponen Anda memanggilnya, dan memberikan nilai yang dapat diterima untuk tingkat kesulitan.

Pada akhirnya, saya pikir Anda harus dapat membuat banyak hal lebih dapat diuji dengan mengambil fungsi-fungsi yang didefinisikan dalam mapDispatch , dan mendefinisikannya secara terpisah. Maka Anda tidak perlu khawatir tentang mapDispatch terhubung untuk menguji komponen Anda dengan benar, dan dapat fokus pada apakah komponen tersebut baru saja memanggil panggilan balik prop yang diberikan dengan argumen yang tepat atau apa.

Juga, dari sudut pandang komponen: pada akhirnya sebenarnya tidak khawatir tentang apakah {action: CHANGE_NAME, name : "Fred"} dikirim. Yang ia tahu hanyalah bahwa itu disebut this.props.onChangeName("Fred") , dan _that_ adalah apa yang harus Anda coba uji - bahwa itu memanggil panggilan balik prop dengan cara yang benar.

@mordra dalam kasus Anda, saya akan mengekspor mapDispatchToProps dan mengujinya secara terpisah. Anda dapat memverifikasi nama properti semuanya benar dan memberikan mata-mata untuk pengiriman untuk menguji pembuat tindakan Anda.

Karena itu, saya cenderung menghindari mapDispatchToProps demi mapActionCreatorsToProps yang memetakan pembuat tindakan yang sudah terbentuk yang dapat dengan mudah saya uji secara terpisah. Dalam hal ini saya hanya menguji bahwa semua alat peraga saya didefinisikan untuk menjaga dari kesalahan ketik impor.

Terakhir, perhatikan bahwa Anda dapat menggunakan rendering normal (tidak dangkal). Anda memerlukan jsdom atau browser nyata untuk itu, tetapi kemudian Anda dapat merender level apa pun.

Tidak tahu apakah akan menaikkan ini dalam reaksi atau repo enzim

Maaf, saya tidak bermaksud repo React atau Enzyme. Maksud saya React Redux yang merupakan perpustakaan yang Anda gunakan.

Terima kasih atas bantuan teman-teman, itu banyak info untuk saya telusuri dan cerna. @markerikson @fshowalter Saya melakukan lebih banyak melihat-lihat dan akan menerima saran Anda untuk mengekspor mapState/Dispatch dan mengujinya secara terpisah, masuk akal untuk menguji panggilan balik yang membangkitkan tindakan yang diharapkan.

@gaearon Komponen stateless tidak merender tanpa dangkal dan hanya membaca sekilas masalah, tampaknya ada masalah saat merender komponen stateful yang memiliki komponen stateless bersarang jadi saya telah membuat catatan mental untuk menghindari jalur itu untuk saat ini.

Saya tidak yakin masalah mana yang Anda maksud. Anda dapat menggunakan rendering dangkal dengan komponen fungsional dengan baik. Render dangkal bekerja hanya satu tingkat dalam (tidak peduli bagaimana komponen didefinisikan). Ini adalah fitur utamanya—tidak berulang sehingga pengujian komponen tetap independen. Jika Anda memiliki masalah khusus dengan rendering dangkal yang dapat Anda produksi ulang, harap laporkan bug ke repo React. Terima kasih!

@gaearon : jadi untuk memperjelas, jika saya melakukan ini:

let wrapper = shallow(<A><B /></A>);

akankah B dirender, atau tidak? Karena saya pikir itulah pertanyaan awalnya - mencoba membuat <Provider> di sekitar komponen yang terhubung untuk menguji koneksi.

Meskipun tidak berhubungan langsung dengan Redux. Saya memecahkan ini dengan memanggil shallow() pada wadah lagi. Itu membuat komponen dalam dengan status toko diteruskan ke dalamnya.

Contoh:

import React from 'react';
import {expect} from 'chai';
import {shallow} from 'enzyme';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import events from 'events';

const mockStore = configureMockStore([ thunk ]);
const storeStateMock = {
  myReducer:{
    someState: 'ABC'
  }
};

let store;
let component;
describe('tests for MyContainerComponent', () => {
  beforeEach(() => {
    store = mockStore(storeStateMock);
    component = shallow(<MyContainerComponent store={store} />).shallow();
  });

  it('renders container', () => {
    expect(component.find('div.somediv')).to.have.length.of(1);
  });
  ...
});

Semoga ini membantu

Haruskah kita membuat cara yang lebih mudah untuk melakukan ini melalui modul khusus?

Sunting: Jadi kita dapat mendeklarasikan connect sebagai salah satu metode untuk Enzyme, seperti component = shallowWithConnect()

@ev1stensberg Ini adalah ide bagus. Sudahkah kita memutuskan apakah ini pendekatannya dan/atau sudah mulai bekerja? Jika tidak, saya ingin berkontribusi.

Bagi siapa saja yang menemukan masalah ini di masa mendatang, inilah pendekatan yang cocok untuk saya: cukup uji komponen biasa dengan sendirinya. Ekspor definisi komponen sebagai ekspor bernama dan komponen terhubung (untuk digunakan di aplikasi Anda) sebagai ekspor default. Kembali ke kode dari pertanyaan awal:

// test
let component = shallow(<Provider store={store}><ContainerComponent /></Provider>);
component.find('#abc'); // returns null

let component = shallow(<Provider store={store}><div id="abc"></div></Provider>);
component.find('#abc'); // returns the div node

// ContainerComponent
export const Component = ({...}) => (<div id="abc"></div>); // I EXPORT THIS, TOO, JUST FOR TESTING
export default connect(..., ...)(Component);

Kemudian lakukan saja

const component = shallow(<Component {...props} />)
// test component

Dan seperti yang telah ditunjukkan orang lain di atas, jika Anda juga memiliki cakupan untuk fungsi yang Anda sambungkan, Anda harus memiliki cakupan penuh untuk komponen Anda secara keseluruhan.

Nah, hal pertama yang perlu diperhatikan di sini adalah filosofi :

  1. Komunikasikan status dari toko ke a
  2. Ubah status melalui tindakan pengiriman berdasarkan acara.

Jika saya baik-baik saja maka Anda hanya perlu menguji 2 hal:

  1. apakah komponen Anda menerima alat peraga yang benar yang dihasilkan dari status (bisa melalui penyeleksi)?
  2. apakah komponen Anda mengirimkan tindakan yang benar?

Jadi ini adalah pendekatan saya

import React from 'react';
import { shallow } from 'enzyme';
import { fromJS } from 'immutable';
import { createStore } from 'redux';
// this is the <Map /> container
import Map from '../index';
// this is an action handled by a reducer
import { mSetRegion } from '../reducer';
// this is an action handled by a saga
import { sNewChan } from '../sagas';
// this is the component wrapped by the <Map /> container
import MapComponent from '../../../components/Map';

const region = {
  latitude: 20.1449858,
  longitude: -16.8884463,
  latitudeDelta: 0.0222,
  longitudeDelta: 0.0321,
};
const otherRegion = {
  latitude: 21.1449858,
  longitude: -12.8884463,
  latitudeDelta: 1.0222,
  longitudeDelta: 2.0321,
};
const coordinate = {
  latitude: 31.788,
  longitude: -102.43,
};
const marker1 = {
  coordinate,
};
const markers = [marker1];
const mapState = fromJS({
  region,
  markers,
});
const initialState = {
  map: mapState,
};
// we are not testing the reducer, so it's ok to have a do-nothing reducer
const reducer = state => state;
// just a mock
const dispatch = jest.fn();
// this is how i mock the dispatch method
const store = {
  ...createStore(reducer, initialState),
  dispatch,
};
const wrapper = shallow(
  <Map store={store} />,
);
const component = wrapper.find(MapComponent);

describe('<Map />', () => {
  it('should render', () => {
    expect(wrapper).toBeTruthy();
  });

  it('should pass the region as prop', () => {
    expect(component.props().region).toEqual(region);
  });

  it('should pass the markers as prop', () => {
    expect(component.props().markers).toEqual(markers);
  });
  // a signal is an action wich will be handled by a saga
  it('should dispatch an newChan signal', () => {
    component.simulate('longPress', { nativeEvent: { coordinate } });
    expect(dispatch.mock.calls.length).toBe(1);
    expect(dispatch.mock.calls[0][0]).toEqual(sNewChan(coordinate));
  });
  // a message is an action wich will be handled by a reducer
  it('should dispatch a setRegion message', () => {
    component.simulate('regionChange', otherRegion);
    expect(dispatch.mock.calls.length).toBe(2);
    expect(dispatch.mock.calls[1][0]).toEqual(mSetRegion(otherRegion));
  });
});

@markerikson apa yang anda maksud dengan:

anda dapat menguji implementasi mapStateToProps Anda

katakanlah Anda mengekspor wadah yang terhubung melalui ekspor ES6. Anda tidak akan memiliki akses ke mapStateToProps pribadi. Apakah Anda berbicara tentang cara lain atau dapatkah Anda lebih spesifik?

@mordrax

Anda dapat memverifikasi nama properti semuanya benar

Jadi, jika Anda mengekspos mapStateToProps Anda dan menjadikannya publik, maka katakanlah Anda merender ContainerComponent Anda dari pengujian Anda termasuk mengirim katakanlah toko palsu dan mengirim dalam prop negara, maka mapStateToProps Anda menerima status itu, memetakannya ke prop. Tapi bagaimana Anda bisa mengujinya dari titik itu? Connect akan memanggil mapStateToProps sehingga menggabungkan alat peraga tetapi di mana jahitannya dan titik mana jahitan dalam kode selama proses/alur di mana Anda dapat mencegat alat peraga yang diatur pada komponen presentasi? Saya kira pendangkalan ganda seperti @guatedude2 mungkin menjadi salah satu cara untuk memeriksa alat peraga yang dipetakan tanpa jahitan apa pun ...

juga di shallow.shallow Anda. Jadi komponen connect() meneruskan apa, pembungkus kan? Dan pembungkus itu yang membungkus komponen presentasi?

@granmoe dapatkah Anda menjelaskan ini secara lebih rinci tentang apa yang sebenarnya Anda maksud:

jika Anda juga memiliki cakupan untuk fungsi yang Anda masukkan ke dalam koneksi, Anda harus memiliki cakupan penuh untuk komponen Anda secara keseluruhan.

Saya juga dengan @tugorez dalam hal _apa_ yang ingin saya uji dan mengapa.

@markerikson contoh yang sangat bagus dengan mata-mata. Itu satu hal yang dapat Anda uji, tetapi Anda akan membutuhkan lebih banyak asser di sana untuk menguji "unit perilaku". Hanya menguji bahwa tindakan mata-mata dipanggil dari mapDispatchToProps tidak benar-benar memberi tahu kami keseluruhan cerita tentang komponen wadah. Itu tidak menegaskan hasil yang diharapkan berdasarkan logika penangan penampung Anda.

Tidak cukup hanya dengan menguji bahwa Anda telah melewati props atau state, Anda ingin menguji perilaku komponen container. Itu berarti menguji dua hal:

1) apakah komponen wadah memasang alat peraga ke komponen presentasi dan jika ya, apakah ada keadaan tertentu yang saya harapkan dimiliki komponen presentasi setelah dirender berdasarkan keadaan tertentu yang diteruskan oleh alat peraga ke komponen presentasi. Siapa tahu, mungkin beberapa pengembang bodoh datang dan menambahkan lebih banyak kode ke mapStateToProps, Anda tidak dapat selalu percaya bahwa itu akan memetakan semuanya dengan benar, itulah gunanya menguji logika wadah. Meskipun sebenarnya tidak ada logika di mapStateToProps, siapa tahu lagi beberapa pengembang datang dan mengiklankan pernyataan if di sana...yah, itu perilaku yang bisa mengacaukan segalanya.

2) apakah logika (perilaku) penangan pengiriman bekerja di wadah.

@dschinkel :

Praktik umum untuk mendefinisikan komponen yang terhubung ke Redux adalah export default connect()(MyComponent) , dan juga export MyComponent sebagai ekspor bernama. Demikian pula, karena mapState hanyalah sebuah fungsi, Anda juga dapat export const mapState = () => {} , lalu mengimpor dan mengujinya secara terpisah.

Adapun pengujian "komponen wadah" vs "komponen presentasi": pemikiran umum di sini adalah bahwa Anda mungkin tidak perlu khawatir tentang pengujian wadah untuk sebagian besar, di mana "wadah" === "output dari connect ". React-Redux sudah memiliki rangkaian pengujian lengkap untuk bagaimana seharusnya berperilaku, dan tidak ada alasan bagi Anda untuk menduplikasi upaya itu. Kami _tahu_ bahwa ia menangani langganan toko dengan benar, memanggil mapState dan mapDispatch , dan meneruskan props ke komponen yang dibungkus.

Apa yang _Anda_ sebagai konsumen perpustakaan akan tertarik adalah bagaimana komponen _Anda_ berperilaku. Seharusnya tidak masalah apakah komponen Anda sendiri mendapatkan props dari pembungkus connect , tes, atau sesuatu yang lain - itu hanya bagaimana komponen itu berperilaku diberikan set props tertentu.

(Juga, FWIW, saya menyadari Anda benar-benar ahli dalam pengujian dan saya tidak, tetapi _memang_ sepertinya Anda sedikit paranoid tentang berbagai hal :) Jika Anda khawatir tentang makhluk mapState tidak sengaja rusak, lalu tulis tes untuk itu dan lanjutkan.)

Sekarang, jika komponen yang Anda bungkus kemudian merender komponen lain yang terhubung di dalamnya, dan terutama jika Anda melakukan rendering penuh alih-alih rendering dangkal, maka mungkin akan lebih mudah untuk menguji sesuatu dengan melakukan const wrapper = mount(<Provider store={testStore}><MyConnectedComponent /></Provider> . Tetapi, secara keseluruhan, pendapat saya adalah bahwa sebagian besar waktu Anda _harus_ dapat menguji fungsi mapState Anda dan komponen "polos" secara terpisah, dan Anda benar-benar tidak boleh mencoba menguji versi yang terhubung hanya untuk memverifikasi bahwa output dari mapState sedang diteruskan ke komponen biasa.

Sebagai referensi, Anda mungkin ingin melihat versi miniatur connect yang ditulis Dan beberapa waktu lalu untuk mengilustrasikan fungsinya secara internal: https://Gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e .

Ya, maksud saya jika Anda mengekspor mapStateToProps dan dispatchStateToProps, itu akan lebih baik. Saya tidak ingin menguji bahwa connect() (kolaborator dalam pengujian saya) berfungsi, pasti tidak. Jadi itu akan menjadi salah satu cara yang jelas untuk melakukannya, ekspor dua metode itu:

contoh-wadah-spec.js

describe('testing mapPropsToState directly', () => {
    it.only('returns expected state', () => {
        const state = {firstName: 'Dave'};
        const output = mapStateToProps(state);

        expect(output.firstName).to.equal('Dave');
    });
});

Wadah

import React from 'react';
import { connect } from 'react-redux'
import Example from './Example';

export const mapStateToProps = (state) => ({
    firstName: state.firstName
})

export default connect(mapStateToProps)(Example);

Presentasi

import React, { Component } from 'react';

export default class Example extends Component {
    render(){
        return (
            <div className='example'>
                {this.props.firstName}
            </div>
        )
    }
}

Kemudian lagi, Anda juga bisa melakukan sesuatu seperti ini dengan rendering _shallow_ dan masih menyelami komponen anak:

contoh-wadah-spec.js

it('persists firstName to presentation component', () => {
        var state = {firstName: "Dave"};
        const container = mount(<ExampleContainer store={fakeStore(state)}/>),
            example = container.find(Example);

        expect(example.length).to.equal(1);
        expect(example.text()).to.equal(state.firstName);
    });

Wadah

import React from 'react';
import { connect } from 'react-redux'
import Example from './Example';

const mapStateToProps = (state) => ({
    name: state.firstName
})

export default connect(mapStateToProps)(Example);

Presentasi

import React, { Component } from 'react';

export default class Example extends Component {
    render(){
        return (
            <div className='example'>
                {this.props.firstName}
            </div>
        )
    }
}

cara lain untuk melakukan ini secara dangkal, adalah dengan melakukan hal yang dangkal ganda. Dangkal pada induk yang dangkal akan memperpendek komponen anak yang membungkusnya, kira-kira seperti ini:

it('get child component by double shallow()', () => {
        const container = shallow(<ExampleContainer store={fakeStore({})}/>),
            presentationalChildComponent = container.find(Example).shallow().find('.example');

        expect(presentationalChildComponent).to.have.length(1);
    });

Jadi itu hanya cara lain, Anda memeriksa hasil komponen presentasi Anda.

Cara mana pun akan berhasil...namun seperti yang Anda katakan mapStateToProps yang diekspor mungkin adalah yang terbaik, karena hanya itu yang saya pedulikan tentang pengujian dalam hal pengaturan status prop.

Saya menyadari Anda benar-benar ahli dalam pengujian dan saya tidak, tetapi sepertinya Anda sedikit paranoid tentang berbagai hal :)

tidak, saya tidak, pengujian yang benar berarti memiliki tes yang baik yang tidak rapuh dan memberikan nilai adalah penting. Pertama, tidak ada seorang pun yang _ahli_. Setiap orang belajar terus menerus, beberapa hanya tidak mau mengakuinya karena ego mereka. Ini disebut Pengerjaan Perangkat Lunak (alias Profesionalisme).

Menguji hanya perilaku dan tidak menguji kolaborator (connect() itu sendiri) seperti yang Anda katakan adalah penting. Mencari tahu itu untuk pertama kalinya dan menguji dengan baik adalah penting. Memastikan Anda tidak menguji kolaborator itu penting dan terkadang membutuhkan diskusi di antara sesama pengembang.

Anda dapat melakukan tes getas, dan penting untuk tidak melakukan tes getas. Pengujian harus dilakukan dengan serius, dan itu berarti pemeriksaan terus-menerus jika Anda mendekatinya dengan benar. Saya hanya mencoba untuk mencari tahu aliran saya. Saya TDD, jadi bagaimana saya memulai, penting, dan tes yang baik penting dengan itu. Jadi tidak seorang pun harus merasa seperti mereka "paranoid" ketika mereka berusaha memahami berbagai cara untuk menguji komponen React.

React Test Repo Baru Datang...
Sebenarnya saya akan segera membagikan repo yang menunjukkan berbagai gaya dan kombinasi serta pendekatan untuk menguji react-redux. Saya pikir itu akan membantu orang karena saya bukan satu-satunya yang memikirkan hal ini. Berkali-kali Anda melihat orang mengajukan pertanyaan yang sama seputar pengujian wadah redux dan komponen lainnya. Dokumen Redux dan juga dokumen react-redux tidak adil dalam hal itu, mereka kekurangan dokumentasi di area pengujian IMO. Itu hanya menggores permukaan, dan kami membutuhkan repo bagus yang menunjukkan berbagai gaya tentang pendekatan pengujian React jadi saya akan segera memasangnya.

If anyone would like to contribute to that repo, please get a hold of me jadi saya bisa menambahkan Anda sebagai kolaborator di dalamnya. Saya ingin orang menambahkan contoh. Saya ingin melihat contoh langsung React Test Utils + mocha , dengan Enzyme , Ava , Jest , tape , dll.

Kesimpulan :

Saya pikir kedua pendekatan yang telah kita diskusikan baik-baik saja. Langsung uji metodenya, atau uji seperti yang saya lakukan di atas, selami. Terkadang Anda tidak ingin mendasarkan pengujian Anda pada metode tertentu karena Anda bisa mendapatkan tes yang rapuh...karena itu mengapa orang-orang di masa lalu tersengat dengan pengujian. Jadi apakah akan menguji satu fungsi atau tidak, apakah itu menguji "unit" tergantung. Terkadang Anda tidak ingin membuat metode menjadi publik, dan mungkin lebih baik menguji kontrak di atas itu dan merahasiakannya. Jadi, selalu penting untuk memikirkan apa yang sering Anda lakukan saat menulis tes. Tidak ada yang salah dengan itu. Menulis tes yang baik tidak mudah.

Itulah yang dipromosikan TDD, sering melakukan itu. Sehingga Anda mendapatkan kode yang lebih ramping, lebih baik, dan tidak rapuh. TDD memaksa Anda untuk berpikir tentang pengujian di depan, bukan nanti. Itulah perbedaannya dan mengapa orang seperti saya ingin memahami aliran dan pendekatan yang berbeda karena saya harus melakukannya ketika saya TDD, itu memaksa saya untuk sering menilai ulang desain dalam potongan kecil, dan itu berarti sering memikirkan pendekatan pengujian saya.

@markerikson terima kasih atas masukan Anda, saya suka berbicara pengujian. Barang bagus. Saya tidak mempertanyakan keahlian Anda, hanya saja saya sendiri belum benar-benar mencoba menguji mapStateToProps sendiri! Anda tidak berbuat buruk karena menjadi non-tester ;). Saya baru mengenal redux, sesederhana itu jadi saya akan memiliki pertanyaan yang tidak hanya "menyentuh permukaan". Kita harus mendiskusikan pengujian pada tingkat yang lebih rendah sehingga orang memahami apa yang mereka lakukan, bagaimana mereka dapat melakukannya, dan apakah mereka mendapat manfaat dari pendekatan mereka. Untuk melakukan itu, Anda harus memahami koneksi, reaksi-redux, penyedia di tingkat yang lebih rendah...sehingga Anda tahu cara "hanya menguji perilaku". Saya belum melihat banyak orang mengekspor mapStateToProps yang juga menarik bagi saya...dan itu membuat saya bertanya-tanya mengapa tidak.

WeDoTDD.com
BTW bagi siapa saja yang tertarik, saya memulai WeDoTDD.com tahun lalu (ditulis dalam React btw). Jika tim tertentu (semua pengembang di tim tertentu) TDD, atau seluruh perusahaan Anda (beberapa perusahaan memiliki pengembang di mana semua TDD terutama perusahaan konsultan), silakan hubungi saya di slack.wedotdd.com

Memperbarui.

setelah bermain lebih banyak dengan menguji komponen yang terhubung dan merenungkan apa yang bekerja dengan baik untuk saya, saya 100% setuju sekarang dengan pernyataan @markerikson di sini:

jika Anda benar-benar merasa perlu menguji komponen yang terhubung, Anda seharusnya dapat merender tanpa perlu khawatir tentang Penyedia atau konteks atau apa pun.

Saya belum melihat kebutuhan untuk memperkenalkan <Provider /> ke dalam pengujian saya dan sepertinya Anda sedang menguji kolaborator jika Anda melakukannya ketika intinya bukan untuk menguji apakah <Provider /> berfungsi. Anda harus menguji apakah perilaku di komponen Anda berfungsi dan tidak masalah apakah wadah Anda mendapatkan penyimpanan melalui konteks. Lewati saja toko sebagai alat peraga..koneksi hanya perlu beberapa cara untuk sampai ke toko, tidak peduli bagaimana (konteks atau alat peraga)...singkirkan penyedia sama sekali dalam pengujian Anda.

Saya juga tidak melihat kebutuhan untuk menggunakan opsi konteks enzim yang merupakan cara lain Anda dapat mempertahankan penyimpanan melalui konteks Bereaksi. Lupakan konteks reaksi sama sekali dalam pengujian Anda, itu sama sekali tidak perlu mengasapi dan membuat pengujian Anda lebih kompleks dan jauh lebih sulit untuk dipertahankan dan dibaca.

Uji fungsi murni mapStateToProps Anda secara langsung dengan mengekspornya dalam file kontainer Anda. Saya menggunakan kombo pengujian mapStateToProps + juga beberapa tes yang menguji wadah saya secara dangkal, memverifikasi bahwa komponen presentasi setidaknya menerima alat peraga yang saya harapkan dan kemudian saya berhenti di situ dengan pengujian alat peraga. Saya juga memutuskan untuk tidak melakukan pendangkalan ganda untuk pengujian komponen wadah saya. TDD memberi saya umpan balik bahwa dengan mencoba melakukannya, tes menjadi terlalu rumit dan Anda mungkin melakukan sesuatu yang salah. "Salah" itu adalah "jangan menggandakan hal-hal dangkal untuk pengujian komponen wadah Anda". Alih-alih, buat rangkaian baru pengujian komponen presentasi yang menguji output komponen presentasi Anda secara mandiri.

Senang mendengar Anda menyelesaikan masalah. (FWIW, komentar saya sebagian besar sebagai tanggapan atas pertanyaan Anda tentang "jahitan" dan "menggali alat peraga", yang benar-benar tampak seperti pergi ke tingkat detail yang tidak perlu dengan sedikit atau tanpa manfaat.)

Menggunakan <Provider> /context _is_ akan diperlukan jika Anda melakukan render penuh dari komponen yang merender komponen lain yang terhubung, karena anak-anak yang terhubung tersebut akan mencari toko dalam konteks.

Jika Anda memiliki saran untuk meningkatkan dokumen Redux saat ini tentang pengujian, tentu saja ajukan PR.

@markerikson poin bagus di gunung. Saya hanya akan melakukan mount jika saya perlu menguji siklus hidup komponen yang saya uji. Saya tidak akan menggunakannya untuk menyelam jauh ke dalam komponen anak... yang tidak lagi memenuhi syarat sebagai pengujian unit, dan pada dasarnya adalah pengujian integrasi dan Anda akan menggabungkan pengujian itu dengan detail implementasi saat Anda harus menguji komponen anak tersebut pada sendiri dalam isolasi, bukan melalui komponen induk.

Pokoknya barang bagus, terima kasih!

Benar, hanya mengatakan bahwa jika Anda _melakukan_ menggunakan mount untuk memeriksa siklus hidup komponen yang dimaksud, dan komponen tersebut merender komponen lain yang terhubung, maka Anda _akan_ memerlukan penyimpanan yang tersedia dalam konteks untuk komponen bersarang tersebut untuk menghindari kesalahan saat komponen yang diuji dirender.

(Sunting: Saya tampaknya agak mengulangi diri saya sendiri, maaf - bahaya menjawab pertanyaan di banyak tempat tanpa selalu kembali melalui utas, atau bahkan menggulir sedikit ke atas.)

ah oke mengerti! ya gak kepikiran...

Terima kasih untuk semua komentar di sini, @markerikson dan @dschinkel! Mereka sangat membantu. Saya sekarang telah memutuskan untuk mengekspor komponen saya yang tidak terhubung dan mengujinya seperti yang saya lakukan yang lain, dan juga menguji mapStateToProps dan mapDispatchToProps secara terpisah. Namun, ada satu kasus yang tidak ditangkap oleh tes ini, dan saat itulah map(State/Dispatch)ToProps tidak benar-benar diteruskan ke panggilan ke connect . Contoh:

import React from 'react';
import { connect } from 'react-redux';

export const mapStateToProps = ({ name }) => ({ name });

export const Example = ({ name }) => <h1>{name}</h1>;

export default connect({ name } => ({ name: firstName }))(Example); // Whoops, didn't actually pass in `mapStateToProps`.

Ini mungkin tidak akan pernah terjadi dalam praktiknya, tetapi bisa, terutama karena linter tidak akan melaporkan mapStateToProps sebagai variabel yang tidak digunakan karena sedang diekspor.

@danny-andrews Bisakah Anda lebih spesifik tentang cara menguji fungsi mapDispatchToProps Anda?

@leizard

Saya tidak menguji mapDispatchToProps atau mapStateToProps secara langsung lagi. Saya telah berubah pikiran sejak utas ini dan menyarankan untuk tidak melakukan itu.

Juga mengujinya, Anda akan mengalami masalah @ danny-andrews tetapi masalahnya lebih pada tes yang baik dan mencoba mengekspos kedua fungsi itu bukanlah ide yang baik. Ini lebih dari pengujian. Anda harus menguji kedua metode tersebut secara tidak langsung dengan menguji perilaku atau dengan menguji alat peraga melalui wadah dangkal Anda sebagai gantinya. Saya menemukan tidak ada alasan untuk mencoba mengekspos metode pribadi itu dan sekarang saya menyadari bahwa itu juga merupakan praktik pengujian yang buruk untuk mencoba melakukannya.

Jadi saya tidak setuju dengan @markerikson , uji melalui komponen Anda yang terhubung.

Menemukan API/kontrak yang tepat untuk diuji dan tidak diuji berlebihan, itulah kuncinya :).

@dschinkel

Anda harus menguji kedua metode itu secara tidak langsung dengan menguji perilaku ...
Jadi saya tidak setuju dengan @markerikson , uji melalui komponen Anda yang terhubung.

Apakah Anda menyarankan agar Anda tidak mengekspor komponen yang tidak terhubung seluruhnya dan hanya menguji komponen yang terhubung? Saya pikir itu adalah "pengujian yang berlebihan." Ini mengikat pengujian Anda secara tidak perlu ke detail implementasi komponen yang sedang diuji. Karena fungsionalitas dari komponen yang tidak terhubung (jika ada, itu adalah kaleng worm lainnya) sama sekali tidak terkait dengan dari mana ia menerima propsnya. Mungkin Anda ingin mengubah komponen itu menjadi komponen presentasi murni di masa mendatang. Jika Anda menguji komponen yang terhubung, Anda harus mengubah semua pengujian Anda untuk tidak menyuntikkan toko lagi tetapi meneruskan alat peraga secara langsung. Jika Anda menguji komponen yang tidak terhubung, Anda tidak perlu melakukan apa pun kecuali menerbangkan pengujian khusus komponen yang terhubung (lebih lanjut tentang itu di bawah).

Saya setuju bahwa Anda tidak boleh langsung menguji mapStateToProps / mapDispatchToProps . Mengekspos metode pribadi itu hanya untuk tujuan pengujian selalu terasa seperti bau kode bagi saya. Sebenarnya, saya pikir ini adalah satu-satunya waktu Anda harus menguji komponen yang terhubung. Jadi, untuk meringkas:

  1. Ekspor komponen yang tidak terhubung dan komponen yang terhubung
  2. JANGAN mengekspor mapStateToProps atau mapDispatchToProps
  3. Uji semua logika komponen melalui komponen yang tidak terhubung
  4. Hanya uji komponen yang terhubung saat menguji interaksi dengan redux (alat peraga data diteruskan dari tempat yang tepat di toko/alat peraga tindakan ditugaskan ke pembuat tindakan yang tepat, dll.).

Satu-satunya overhead tambahan dengan melakukan nomor 4 adalah Anda harus mematikan toko redux untuk meneruskannya ke komponen Anda yang terhubung. Ini cukup mudah, karena API hanyalah tiga metode.

Metode MapStateToProps

TestContainer.jsx:

import { connect } from 'react-redux';

export const TestContainer = ({ permissions }) => (permissions['view-message'] ? 'Hello!' : null);

export const mapStateToProps = ({ auth }) => ({ permissions: auth.userPermissions });

export default connect(mapStateToProps)(TestContainer); // Ewww, exposing private methods just for testing

TestContainer.spec.jsx:

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

import TestContainer, { mapStateToProps } from './TestContainer';

it('maps auth.userPermissions to permissions', () => {
  const userPermissions = {};

  const { permissions: actual } = mapStateToProps({
    auth: { userPermissions },
  });

  expect(actual).toBe(userPermissions);
});

Metode Baru

TestContainer.jsx:

import { connect } from 'react-redux';

export const TestContainer = ({ permissions }) => (permissions['view-message'] ? 'Hello!' : null);

const mapStateToProps = ({ auth }) => ({ permissions: auth.userPermissions });

export default connect(mapStateToProps)(TestContainer);

TestContainer.spec.jsx:

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

import TestContainer, { TestComponent } from './TestContainer';

it('renders TestComponent with approriate props from store', () => {
  const userPermissions = {};
  // Stubbing out store. Would normally use a helper method. Did it inline for simplicity.
  const store = {
    getState: () => ({
      auth: { userPermissions },
    }),
    dispatch: () => {},
    subscribe: () => {},
  };
  const subject = shallow(<TestContainer store={store} />).find(TestComponent);

  const actual = subject.prop('permissions');
  expect(actual).toBe(userPermissions);
});

ya itulah yang saya katakan saya tidak mengekspor dan menguji mapStateToProps , ini sudah selesai pengujian. Uji melalui API . API di sini adalah Container .

@danny-andrews

Uji semua logika komponen melalui komponen yang tidak terhubung

Anda dapat situs beberapa contoh?

Maksud saya sama seperti Anda ketika Anda berkata: "Anda harus menguji kedua metode itu secara tidak langsung dengan menguji perilakunya"

Mungkin membantu untuk memperjelas istilah: ketika saya mengatakan "komponen tidak terhubung," maksud saya komponen mentah yang dibungkus dalam panggilan ke connect dan ketika saya mengatakan "komponen terhubung" maksud saya komponen yang dihasilkan yang dikembalikan dari panggilan ke connect . "Komponen yang terhubung" dan "wadah" yang saya yakini adalah sinonim. Dari saya di atas:

// Unconnected component. Test all component logic by importing this guy.
export const TestComponent = ({ permissions }) => (permissions['view-message'] ? 'Hello!' : null);

const mapStateToProps = ({ auth }) => ({ permissions: auth.userPermissions });

// Connected component (container). Only test interaction with redux by importing this guy.
export default connect(mapStateToProps)(TestComponent);

Beberapa orang lebih suka membagi ini seluruhnya dan "wadah" mereka hanya dipanggil ke connect yang membungkus komponen presentasi murni. Dalam hal ini, nomor 1 dan 3 dalam daftar hilang dan satu-satunya tes yang perlu Anda tulis adalah tes yang memverifikasi interaksi dengan redux (nomor 4).

Saya berencana untuk menulis posting blog BESAR tentang ini. Saya sedang sibuk saat ini dalam kode tetapi akan membalas nanti

Setelah membaca utas ini beberapa kali, saya sampai pada kesimpulan bahwa ada 3 pendekatan yang disarankan:

  1. Ekspor mapDispatchToProps dan mapStateToProps dan uji secara terpisah
  2. Render komponen Wadah yang dangkal dan uji kabelnya dengan benar
  3. Gunakan pemilih alih-alih mapStateToProps dan pembuat tindakan alih-alih mapDispatchToProps dan uji secara terpisah; tulis tes menggunakan pembuat tindakan, reduksi, dan pemilih bersama-sama untuk memastikan seluruh alur berfungsi.

Pada akhirnya, saya kira semua opsi valid, tetapi memiliki pro dan kontra sendiri. Yang "paling murni" mungkin yang ke-2, tetapi juga melibatkan sebagian besar pekerjaan. Preferensi pribadi saya benar-benar akan menuju opsi 3.
Saya menulis lebih detail tentang ini di blog saya , di mana saya juga memiliki beberapa contoh kode.

@ucorina
Di dunia modul ES6 saat ini, setiap file dianggap sebagai modul yang dibungkus ke dalam ruang lingkupnya sendiri. Mengekspor adalah API Anda dari modul kecil dan rasanya sangat salah untuk mengekspor metode, hanya agar Anda dapat mengujinya. Ini mirip dengan bagaimana Anda akan membuat metode pribadi menjadi publik, hanya untuk mengujinya.

Itu sebabnya saya condong ke pendekatan @dschinkel dan tidak mengekspor mapDispatch dan mapState, karena bagi saya itu adalah implementasi pribadi dari komponen yang terhubung.

Namun saya tidak yakin apa yang harus dilakukan dengan komponen, yang hanya membungkus komponen lain di sekitar koneksi.

Apakah Anda pernah menulis posting blog? @dschinkel akan senang membacanya

@AnaRobynn

Ya, saya setuju dengan Anda bahwa hanya mengekspor mapDispatch dan mapState terasa salah, dan saya mungkin tidak akan menggunakannya sendiri, namun ini adalah opsi yang valid, meskipun opsi yang kurang "murni" . Saya meninggalkannya di sana sebagai opsi yang memungkinkan juga karena Dan Abramov menyarankan teknik yang tepat ini di sini: https://github.com/reduxjs/react-redux/issues/325#issuecomment -199449298 .

Untuk menjawab pertanyaan Anda tentang "apa yang harus dilakukan dengan komponen, yang hanya membungkus komponen lain di sekitar koneksi", saya akan mengatakan jangan mengujinya. Tidak ada logika bisnis yang terkait dengan aplikasi Anda dan Anda tidak ingin menguji implementasi connect - yang sudah diuji di perpustakaan react-redux .

Jika Anda masih ingin menguji, untuk membuktikan kode Anda di masa mendatang, Anda selalu dapat mengikuti pendekatan ke-2 yang saya jelaskan di sini yang lebih dekat dengan apa yang dijelaskan oleh @dschinkel .

@ucorina Tepat, saya pikir opsi adalah yang akan saya pilih juga! Terima kasih telah menjelaskan jawaban Anda lebih dalam. Ini adalah posting blog yang ditulis dengan baik. Selamat!

@ucorina Maaf mengganggu lagi, tapi setelah tidur sebentar saya tidak yakin saya sepenuhnya setuju dengan opsi 2 juga. Aku tidak yakin. Saya juga tidak yakin tentang manfaat pengujian mapStateToProps sama sekali.

Apakah menurut Anda baik-baik saja "Opsi 2", secara tidak langsung juga menguji pembuat aksi? Dan juga secara tidak langsung menguji penyeleksi? Apakah itu hal yang baik?
Saya tidak 100% yakin dengan itu, tetapi saya juga bingung dengan semua informasi (yang salah) di luar sana. Ada begitu banyak ide yang berbeda untuk pengujian.

  1. Hanya gunakan pemilih di dalam mapStateToProps

    • mengejek mereka selama tes kontainer jika diperlukan

    • Uji pemilih secara terpisah.

    • Hanya gunakan pembuat tindakan di dalam mapDispatchToProps

    • mengejek mereka selama tes kontainer jika diperlukan.

    • Uji pembuat tindakan secara terpisah.

    • Dangkal komponen yang terhubung

    • menegaskan itu diberikan

    • uji logika tambahan apa pun yang harus ditambahkan ke mapStateToProps , mapDispatchToProps atau mergeProps dan belum diuji di tempat lain. (pengiriman bersyarat, penyeleksi yang mengambil argumen dari ownProps , dll)



      • dalam hal ini Anda hanya menguji tiruan yang disebut jumlah waktu yang benar dan dengan argumen yang benar.



Alternatif:

  • Status toko tiruan untuk menguji wadah + pemilih bersama-sama
  • Mengejek panggilan api luar untuk menguji wadah + tindakan bersama

@AnaRobynn Saya percaya pendekatan ke-2 @ucorina adalah yang benar.

Banyak dari apa yang saya dengar menyarankan:

  1. Anda harus mengekspos mapDispatchToProps, dan mengujinya secara langsung
  2. Anda tidak perlu menguji panggilan Anda ke connect() karena connect() sudah diuji

Mengenai 1, saya pikir itu bukan praktik yang baik untuk mengekspos internal untuk menguji. Pengujian Anda A: sekarang mengasumsikan struktur internal, dan B: harus menguji hal bagaimana mereka akan benar-benar digunakan.

Mengenai 2, Anda _absolutely_ harus menguji sesuatu yang memanggil perpustakaan eksternal. Ini adalah salah satu titik lemah dalam aplikasi apa pun untuk melanggar. Dengan pustaka apa pun, masih mungkin untuk memperkenalkan perubahan yang melanggar pada versi minor/tambalan, dan Anda _ingin_ pengujian Anda gagal dengan cepat.

Apakah menurut Anda baik-baik saja "Opsi 2", secara tidak langsung juga menguji pembuat aksi? Dan juga secara tidak langsung menguji penyeleksi? Apakah itu hal yang baik?

Ya, cakupan tes yang berlebihan adalah hal yang baik.

@philihp @dougbacelar
Saya percaya saya tumbuh sedikit terkait dengan pengujian aplikasi React. Keyakinan dan pemikiran saya saat ini tentang subjek:

  1. Jangan mengekspos mapStateToProps dan mapDispatchToProps jika Anda tidak memerlukan pemetaan tersebut di beberapa komponen. Mengekspornya hanya untuk menguji adalah anti-pola.
  2. Saat ini saya melihat wadah sebagai kolaborator (fungsi yang mendelegasikan fungsi lain dan fungsi lain itu melakukan logika nyata).
    Apa artinya?
  3. Kontainer tidak melakukan logika apa pun
  4. Logika untuk memilih status adalah untuk fungsi pemilih (yang dapat diuji unit secara murni)
  5. Logika untuk tindakan disembunyikan di pembuat tindakan (yang mungkin tergantung apakah Anda menggunakan redux-thunk atau tidak adalah kolaborator sendiri)
  6. Tampilan yang dirender adalah fungsi lain (yang berpotensi memiliki fungsi kolaborator lain atau hanya merender barang

=> Pengaturan pengujian untuk itu sebenarnya cukup mudah ketika Anda menggunakan perpustakaan pengujian yang tepat (saya sarankan testdouble.js).
=> Mengejek pemilih dan tindakan Anda melalui testdouble.js (menggunakan td.when() )
=> Render dangkal komponen container Anda, dive() sekali untuk mendapatkan akses ke metode komponen tampilan
=> Diberikan seperangkat aturan oleh td.when() menegaskan jika komponen berperilaku dengan benar

Tidak perlu menyuntikkan beberapa perpustakaan fakeStore, tidak apa-apa untuk mematikan toko.

Contoh:

/** <strong i="27">@format</strong> */

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

function setup(props) {
  const HasOrganization = require('./HasOrganization').default;
  const defaultProps = {
    store: {
      subscribe: Function.prototype,
      getState: Function.prototype,
      dispatch: Function.prototype,
    },
  };
  const container = shallow(<HasOrganization {...defaultProps} {...props} />);
  return {
    container,
    wrapper: container.dive(),
  };
}

describe('<HasOrganization /> collaborations', () => {
  let getCurrentOrganizationId;
  beforeEach(() => {
    getCurrentOrganizationId = td.replace('../containers/App/userSelectors')
      .selectCurrentOrganizationId;
  });

  test('not render anything when the id is not valid', () => {
    const Dummy = <div />;
    td.when(getCurrentOrganizationId()).thenReturn(null);
    const { wrapper } = setup({ children: Dummy });

    expect(wrapper.find('div').length).toBe(0);
  });

  test('render something when the id is valid', () => {
    const Dummy = <div />;
    td.when(getCurrentOrganizationId()).thenReturn(1);
    const { wrapper } = setup({ children: Dummy });

    expect(wrapper.find('div').length).toBe(1);
  });
});

Pikiran?

@AnaRobynn Sangat setuju dengan poin 1 dan 2!

Wadah saya biasanya sangat lurus ke depan:

// imports hidden

const mapStateToProps = (state, { userId }) => ({
  isLoading: isLoading(state),
  hasError: hasError(state),
  placeholders: getPlaceholders(userId)(state), // a list of some sort
  shouldDoStuff: shouldDoStuff(state),
});

const mapDispatchToProps = {
  asyncActionPlaceholder,
};

export const mergeProps = (stateProps, dispatchProps, ownProps) => {
  return {
    ...stateProps,
    ...dispatchProps,
    ...ownProps,
    onMount: () => {
      if (stateProps.shouldDoStuff) {
        dispatchProps.asyncActionPlaceholder(ownProps.userId);
      }
    },
  };
};

export default compose(
  connect(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps
  ),
  callOnMount(props => props.onMount())
)(SampleComponent);

Jadi saya akan

  1. memata-matai semua pemilih dan pembuat aksi
  2. test getPlaceholders spy dipanggil dengan userId yang benar
  3. menegaskan SampleComponent telah dirender dengan alat peraga yang benar
  4. panggil prop onMount dari SampleComponent dan verifikasi bahwa itu mengirimkan tindakan tiruan ketika shouldDoStuff===true
  • Hal-hal seperti rendering bersyarat dari barang-barang tergantung pada alat peraga yang akan saya uji pada tes terpisah untuk SampleComponent
  • Saya juga lebih suka menggunakan redux-mock-store untuk mengejek toko seperti configureMockStore([thunk])() tetapi saya pikir tidak apa-apa untuk tidak melakukannya...

TL; DR Saya pada dasarnya hanya penyeleksi uji yang dipanggil dengan argumen yang benar, tindakan yang diejek berhasil dikirim (jika bergantung pada logika apa pun) dan bahwa komponen anak dirender dengan alat peraga yang benar

@philihp

  • Saya sangat menentang pengujian perpustakaan eksternal, saya lebih suka mengejek semuanya dan menguji hal-hal secara terpisah.
  • Saya merasa aman percaya bahwa tes ujung ke ujung akan mengatasi masalah besar apa pun yang disebabkan oleh pemutakhiran perpustakaan.
  • Alasan saya tidak suka menguji wadah+pemilih+pembuat tindakan secara bersamaan adalah karena Anda harus menguji ulang pemilih dan pembuat tindakan yang sama untuk setiap wadah lain yang menggunakannya kembali. Pada saat itu Anda lebih baik menambahkan lebih banyak kasus uji ke unit pengujian itu sendiri.

@dougbacelar Bisakah Anda memberikan beberapa contoh kode uji untuk menguji komponen yang Anda tunjukkan. Anda menggunakan enzim? Sesuatu yang lain? Dangkal? Gunung?

@dougbacelar

Saya sangat menentang pengujian perpustakaan eksternal, saya lebih suka mengejek semuanya dan menguji hal-hal secara terpisah.

Mengolok-olok ketika Anda perlu, tetapi jika Anda dapat menggunakan yang asli, mengapa tidak? Mengejek berguna ketika panggilan tertentu lambat, atau memiliki efek samping seperti permintaan jaringan.

Alasan saya tidak suka menguji wadah+pemilih+pembuat tindakan secara bersamaan adalah karena Anda harus menguji ulang pemilih dan pembuat tindakan yang sama untuk setiap wadah lain yang menggunakannya kembali. Pada saat itu Anda lebih baik menambahkan lebih banyak kasus uji ke unit pengujian itu sendiri.

Ya , uji pemilih Anda, pembuat tindakan secara mandiri. Uji mereka secara menyeluruh dengan banyak masukan yang diharapkan.

Ya , uji juga wadah Anda. Jika itu berarti mereka memberikan cakupan yang tumpang tindih pada pemilih dan pembuat tindakan, itu bukan hal yang buruk.

Berikut yang saya maksud...

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { saveColor } from '../actions/save';
import { selectColors } from '../reducers/colors.js';
import ColorButtons from '../components/ColorButtons';
const ColorButtons = ({ colors, onClick }) => (
  <div>
    {colors.map(color => {
      <button type="button" key={color} onClick={onClick(color)}>{color}</button>
    })}
  </div>
);
ColorButtons.propTypes = {
  colors: PropTypes.arrayOf(PropTypes.string),
  onClick: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({
  colors: selectColors(state.colors),
});
const mapDispatchToProps = dispatch => ({
  onClickColor: color => () => {
    dispatch(saveColor({ color }));
  },
});
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(ColorButtons);
import React from 'react';
import { shallow } from 'enzyme';
import configureStore from 'redux-mock-store';
import ColorButtons from '../ColorButtons';
import { saveColor } from '../../actions/save';
import { selectColors } from '../../reducers/colors.js';
const buildStore = configureStore();
describe('<ColorButtons />', () => {
  let store;
  let wrapper;
  const initialState = { colors: ['red', 'blue'] };
  beforeEach(() => {
    store = buildStore(initialState);
    wrapper = shallow();
  });
  it('passes colors from state', () => {
    expect(wrapper.props().colors).toEqual(selectColors(initialState.colors));
  });
  it('can click yellow', () => {
    const color = 'yellow';
    wrapper.props().onClick(color)();
    expect(store.getActions()).toContainEqual(saveColor({ color }));
  });
});

Di sini, ini tidak menguji semua parameter berbeda yang dapat diperoleh selectColors dan saveColor ; itu menguji bahwa ketika <ColorButtons /> dibuat, ia mendapatkan properti yang kita harapkan. Benar-benar menguji mereka di tes lain.

import { selectColors } from '../colors.js';
describe('selectColors', () => {
  it('returns the colors', state => {
    expect(selectColors(['red'])).toEqual(['red'])
  })
  it('returns an empty array if null', state => {
    expect(selectColors(null)).toEqual([])
  })
  it('returns a flattened array', state => {
    expect(selectColors(['red', ['blue', 'cyan']])).toEqual(['red','blue','cyan'])
  })
})

Saya sangat menentang pengujian perpustakaan eksternal, saya lebih suka mengejek semuanya dan menguji hal-hal secara terpisah.

Mengolok-olok ketika Anda perlu, tetapi jika Anda dapat menggunakan yang asli, mengapa tidak? Mengejek berguna ketika panggilan tertentu lambat, atau memiliki efek samping seperti permintaan jaringan.

Tidak setuju, sebagian.
Saya tidak pernah mengejek perpustakaan eksternal (jangan mengejek apa yang tidak Anda miliki), tetapi saya menulis pembungkus misalnya axios. Dalam tes kolaborasi saya, saya hanya bisa mengejek semua fungsi dan memastikan semuanya terhubung dengan benar.

Dalam tes kolaborasi saya, saya selalu mengejek modul, fungsi, dll
Fungsi-fungsi ini dipanggil oleh kolaborator Anda dapat dengan mudah diuji unit.

Berikut yang saya maksud...

Juga tidak setuju.
Anda secara implisit menguji reduksi dan penyeleksi Anda di sini. Jika itu yang ingin Anda lakukan dengan baik, tetapi saya lebih suka mengejek fungsi-fungsi ini dan hanya memeriksa apakah mereka dipanggil dengan benar. Sisanya ditangani oleh reduksi/selektor.

Utas ini mengulangi poin yang dibuat di https://martinfowler.com/articles/mocksArentStubs.html , Pengujian Klasik dan Mockist

Sepakat. Mengunci.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat