Redux: Menguji komponen terhubung yang memiliki komponen terhubung bersarang di dalamnya

Dibuat pada 10 Nov 2016  ·  21Komentar  ·  Sumber: reduxjs/redux

Hai, ini lebih merupakan pertanyaan daripada masalah.

Saya baru-baru ini mulai menggunakan React dan Redux untuk sebuah proyek dan saya mengalami beberapa masalah dengan pengujian tetapi dokumentasinya tampaknya tidak memberikan solusi konklusif untuk masalah tersebut.

Saat ini saya memiliki komponen terhubung yang ingin saya tulis beberapa pengujian unit, namun ada komponen terhubung yang bersarang di dalam komponen pertama dan saya tampaknya akhirnya diblokir oleh props atau status (atau dalam banyak kasus keduanya) dari komponen bersarang tidak disetel.

Saya ingin tahu apakah ada cara untuk mematikan satu komponen sehingga saya dapat fokus hanya pada komponen yang saya coba uji?

Terima kasih sebelumnya

question

Komentar yang paling membantu

Perhatian yang diangkat di sini adalah ketika komponen yang diuji kemudian merender komponen lain yang terhubung, baik sebagai turunan langsung atau di suatu tempat di bawah hierarki. Jika Anda melakukan render lengkap melalui mount() , maka komponen turunan yang terhubung benar-benar akan mencoba mengakses this.context.store , dan tidak menemukannya. Jadi, opsi utamanya adalah memastikan turunan tidak dirender dengan melakukan pengujian yang dangkal, atau benar-benar membuat toko tersedia dalam konteks.

Semua 21 komentar

Dua hal:

1) ini adalah penggunaan dan mungkin harus pergi ke stack overflow.

2) bagian dari kenyamanan menggunakan komponen pesanan connect lebih tinggi
adalah bahwa Anda dapat menulis pengujian unit untuk komponen unconnected dan
percaya redux untuk menangani pemipaan toko sebagaimana mestinya

Jika tidak jelas apa yang saya maksud dengan itu saya dapat memposting contoh.

Beberapa pemikiran:

  • Salah satu cara untuk fokus hanya pada satu komponen adalah dengan menggunakan rendering "dangkal", yang sebenarnya tidak merender turunan apa pun
  • Anda juga dapat merender <Provider store={testStore}><ConnectedComponent /></Provider> dalam pengujian Anda

FYI, saya memiliki link ke sejumlah artikel tentang pengujian React / Redux di sini, yang mungkin berguna bagi Anda: https://github.com/markerikson/react-redux-links/blob/master/react-redux-testing. md .

Dan ya, sebagai pertanyaan penggunaan, ini lebih baik ditanyakan di tempat lain.

Terima kasih @markerikson Saat ini saya mencoba metode terakhir yang Anda sebutkan tetapi juga akan menandai daftar tautan Anda :)

Saya masih berpikir jika Anda ingin menguji komponen sekali itu dalam isolasi, maka Anda lebih baik melakukan sesuatu seperti

// Component.js
import React from 'react'

export const Component = ({ a, b, c }) => ( 
  // ...
)

// ...

export default connect(mapStateToProps, mapDispatchToProps)(Component)

maka Anda bisa memasukkan komponen sebelum terhubung dalam pengujian Anda seperti

// Component.spec.js
import { Component } from './Component.js'
import { mount } from 'enzyme'

it('should mount without exploding', () => {
  mount(<Component a={1} b={2} c={3} />)
})

Jika yang benar-benar ingin Anda uji adalah bahwa perubahan pada toko Anda menyebar dengan benar daripada masuk akal untuk merender Provider dan itu berbeda, meskipun saya tidak yakin apakah Anda mendapatkan banyak nilai dari pengujian itu.

Perhatian yang diangkat di sini adalah ketika komponen yang diuji kemudian merender komponen lain yang terhubung, baik sebagai turunan langsung atau di suatu tempat di bawah hierarki. Jika Anda melakukan render lengkap melalui mount() , maka komponen turunan yang terhubung benar-benar akan mencoba mengakses this.context.store , dan tidak menemukannya. Jadi, opsi utamanya adalah memastikan turunan tidak dirender dengan melakukan pengujian yang dangkal, atau benar-benar membuat toko tersedia dalam konteks.

@markerikson Ah ok, saya baru saja membaca ulang saya pikir OP mencoba menguji komponen dalam daripada bagian luar. 👍

Terima kasih atas bantuan Anda sejauh ini guys,

Masalah utama yang saya alami saat ini adalah bahwa saya tidak dapat meneruskan status ke komponen terhubung yang ada di dalamnya, sehingga tampilan tidak ditampilkan dengan benar.

Secara umum, keadaan mengejek adalah sesuatu yang sebenarnya telah saya perjuangkan cukup banyak dan belum dapat menemukan sesuatu yang meyakinkan tentang masalah ini, jadi akan sangat bagus jika seseorang dapat memberi saya gambaran tentang bagaimana saya akan melewati keadaan yang diejek ke a komponen yang terhubung atau lebih tinggi bersarang?

TIA

@StlthyLee : apa yang Anda maksud dengan "lulus negara"? Jika Anda merender <Provider store={store}><ComponentWithConnectedDescendants /></Provider> dalam pengujian Anda, itu seharusnya menangani banyak hal.

Itulah masalahnya, hanya melakukan itu tidak menanganinya dengan benar, komponen yang disarangkan di dalamnya memerlukan parameter tertentu dari status aplikasi yang saat ini tidak didapatkan karena satu dan lain alasan.

@StlthyLee mungkin harus memberikan contoh / gist / codepen / repo-link atau semacamnya, itu akan memakan waktu lebih sedikit untuk menemukan masalah karena tampaknya ada bentrokan terminologi di sini.

@Kok semoga ini membantu memperjelas

https://gist.github.com/StlthyLee/02e116d945867a77c382fa0105af1bf3

jika tidak, silakan tanyakan, karena ini adalah sesuatu yang ingin saya selidiki, setelah menghabiskan 9-10 tahun terakhir bekerja di dunia Rails dan memiliki pemahaman yang baik tentang TDD dari perspektif itu, saya sekarang mencoba untuk mendapatkan kepalaku di sekitar TDD di dunia React / Redux.

Jadi saya yakin saya telah sampai ke dasar masalah ini sekarang, itu bukan masalah pengujian tetapi lebih banyak salinan dan kesalahan masa lalu yang dibuat oleh anggota tim lain, saya rasa ini adalah bagian tak terpisahkan dari keharusan menulis tes agar sesuai dengan kode daripada cara TDD pilihan saya. Terima kasih atas semua informasi yang diberikan di sini, ini sangat berharga

Hanya untuk menawarkan opsi lain yang dapat berfungsi untuk beberapa kasus penggunaan, saya baru-baru ini mengalami masalah ini dengan komponen terhubung bersarang. Alih-alih membuat toko tiruan, saya mengekspor versi yang tidak terhubung dari setiap komponen untuk mengujinya tanpa penyimpanan. Sesuatu seperti ini:

class TopLevelImpl extends React.PureComponent {
  // Implementation goes here
}

// Export here for unit testing.  The unit test imports privates and uses TopLevel
// with Enzyme's mount().
export const privates = {
  TopLevel: TopLevelImpl,
}

export const TopLevel = connect(mapStateToProps)(TopLevelImpl)

Saya melakukan hal yang sama untuk komponen bersarang anak sehingga masing-masing dapat diuji secara independen.

Kemudian saya mengambil komponen terhubung tingkat atas dan meneruskan komponen yang terhubung ke anak sebagai alat peraga dalam fungsi render() aplikasi. Ketika unit menguji komponen tingkat atas, alih-alih meneruskan komponen yang terhubung, saya hanya meneruskan komponen tiruan untuk memastikan bahwa mereka dirender di tempat yang tepat. Anda juga bisa memasukkan komponen yang tidak terhubung.

Sesuatu seperti ini:

// Root render function
render() {
  return (
    <Provider store={store}>
      <TopLevel child1={<Child1 />} child2={<Child2 />} />
    </Provider>
  )
}

Saya sebenarnya menyukai ide Anda. Tapi saya melakukan sesuatu seperti ini

const props = {
  child1: () => (<div />),
  child2: () => (<div />)
}
<TopLevel {...props} />

Saya pikir jika Anda sudah menguji child1 dan child2 maka mungkin tidak perlu memilikinya di komponen TopLevel Anda.

Saya meneruskan fungsi juga jika saya perlu merender komponen secara kondisional.

Saya tidak mengikuti kalimat terakhir Anda. Apa pengujian harus dilakukan dengan meneruskan komponen anak ke komponen lain?

Baik. Misalnya saya memiliki komponen terhubung Eksternal yang menggunakan satu atau beberapa komponen terhubung Internal . Dalam hal ini jika saya ingin menguji komponen Eksternal saya melakukan sesuatu seperti ini

wrapper = mount(<ExternalComponent.WrappedComponent {...props} />)

Namun, saya mendapat kesalahan bahwa komponen internal saya perlu status agar dapat berjalan dengan baik.

Invariant Violation: Could not find "store" in either the context or props

Kemudian saya mencoba untuk melewati keadaan tiruan dll. Berhasil. Namun dalam kasus saya, saya harus melakukan banyak inisialisasi untuk setiap komponen yang terhubung, termasuk panggilan async, initialState dll dan saya memiliki kesalahan lain yang terkait dengan itu.

Saya pikir ini akan menjadi berlebihan untuk melakukan semua inisialisasi yang tepat untuk semua komponen yang terhubung internal Jika saya hanya ingin menguji komponen Eksternal.

Jadi saya telah memutuskan untuk memindahkan semua komponen yang terhubung keluar dan meneruskannya sebagai alat peraga ke komponen terhubung Eksternal. Dan sekarang saya bisa menggantinya dengan div kosong ketika saya ingin menguji.

Oh ya, itu hanya motivasi asli untuk polanya. Jadi untuk mengubah kalimat Anda, yang Anda maksud adalah "Saya pikir jika Anda telah menguji child1 dan child2, maka tidak perlu meneruskannya ke komponen TopLevel Anda saat unit mengujinya."

Sepakat.

@StlthyLee , Sayangnya saya menghadapi masalah yang sama ketika mencoba menguji komponen yang memiliki komponen terhubung bersarang.

Solusi yang menurut saya sangat berguna adalah menghubungkan komponen bersarang ke penyimpanan redux tergantung pada lingkungan pengujian.

  • Saat pengujian dijalankan, siapkan variabel lingkungan NODE_ENV="test" . Anda dapat menangkap variabel ini dan membuat komponen menerima properti default. Dalam kasus seperti itu, komponen tidak terhubung ke penyimpanan, dan saat menguji komponen induk tidak ada masalah yang muncul.
  • Dalam kasus lain buatlah komponen terhubung. Ini adalah skenario default saat menjalankan komponen dalam aplikasi.

a) Pertama, Anda memerlukan fungsi pembantu yang menentukan apakah lingkungannya test :

export default function ifTestEnv(forTestEnv, forNonTestEnv) {
  const isTestEnv = process && process.env && process.env.NODE_ENV === 'test';
  const hoc = isTestEnv ? forTestEnv : forNonTestEnv;
  return function(component) {
    return hoc(component);
  };
}

b) Selanjutnya, defaultProps() dan connect() harus diterapkan secara bersyarat tergantung pada lingkungan:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import defaultProps from 'recompose/defaultProps';

import ifTestEnv from 'helpers/if_test_env';
import { fetch } from './actions';

class MyComponent extends Component {
   render() {
      const { propA, propB } = this.props;
      return <div>{propA} {probB}</div>
   }

   componentDidMount() {
      this.props.fetch();
   }
}

function mapStateToProps(state) {
  return {
    propA: state.valueA,
    propB: state.valueB
  };
}

export default ifTestEnv(
  defaultProps({
    propA: 'defaultA',
    propB: 'defaultB',
    fetch() {   }
  }),
  connect(mapStateToProps, { fetch })
)(MyComponent);

c) Pastikan bahwa NODE_ENV adalah "test" saat tes dijalankan:

  • Saat menggunakan mocha CLI, letakkan awalan untuk menyetel lingkungan pengujian: NODE_ENV='test' mocha app/**/*.test.jsx .
  • Untuk webpack, terapkan plugin:
plugins: [
    // plugins...
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('test')
      },
    })
]

@markerikson Terima kasih atas dua saran. Saya mencoba yang kedua dan membungkus komponen saya untuk diuji dengan Penyedia. Dalam hal ini mapStateToProps berfungsi dengan benar tetapi saya tidak mendapatkan hasil yang diharapkan dengan mapDispatchToProps . Di sini komponen yang diuji merender komponen lain yang terhubung.

MapDispatchToProps saya terlihat seperti ini

/* <strong i="10">@flow</strong> */
import { bindActionCreators } from 'redux';

import type { Dispatch } from './types';
import * as actions from './actions';

export default (dispatch: Dispatch, ownProps: Object): Object => ({
  actions: bindActionCreators(actions, dispatch),
});

di sini nilai tindakan yang dikembalikan adalah undefined .

store.js

import { applyMiddleware, compose, createStore } from 'redux';
import { autoRehydrate } from 'redux-persist';
import thunk from 'redux-thunk';

import rootReducer from './reducers';
import { REHYDRATE } from 'redux-persist/constants';

const store = compose(autoRehydrate(), applyMiddleware(thunk, createActionBuffer(REHYDRATE)))(createStore)(rootReducer);

export default store;

Saya menyadari ini adalah utas yang cukup lama tetapi saya menemukan ini menjadi sedikit masalah gangguan dan berpikir solusi ini dapat membantu seseorang yang tersandung pada utas ini (seperti saya).

Karena saya tidak benar-benar menguji apa pun yang terkait dengan Redux, solusi sederhana yang saya temukan adalah meniru fungsi koneksi untuk mengembalikan hanya komponen yang tidak diubah yang diteruskan ke sana.

Misalnya dalam aplikasi Anda, Anda mungkin memiliki komponen:


export class ExampleApp extends Component {
  render() {
  }
}

export default connect(
  null,
  { someAction }
)(ExampleApp);

kemudian di awal file pengujian Anda, Anda dapat menempatkan fungsi koneksi tiruan untuk menghentikan interaksi Redux dengan komponen Anda:

jest.mock("react-redux", () => {
  return {
    connect: (mapStateToProps, mapDispatchToProps) => (
      ReactComponent
    ) => ReactComponent
  };
});

Pemalsuan ini perlu ditempatkan di setiap file pengujian yang memasang anak yang terhubung.

@ scottbanyard bagaimana Anda meneruskan props ke komponen anak?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

benoneal picture benoneal  ·  3Komentar

captbaritone picture captbaritone  ·  3Komentar

ramakay picture ramakay  ·  3Komentar

ms88privat picture ms88privat  ·  3Komentar

vslinko picture vslinko  ·  3Komentar