Redux: اختبار المكونات المتصلة التي تحتوي على مكونات متصلة متداخلة بداخلها

تم إنشاؤها على ١٠ نوفمبر ٢٠١٦  ·  21تعليقات  ·  مصدر: reduxjs/redux

مرحبًا ، هذا سؤال أكثر منه مشكلة.

لقد بدأت مؤخرًا في استخدام React و Redux في مشروع وأواجه مشكلتين في الاختبار ولكن لا يبدو أن الوثائق تقدم حلاً نهائيًا للمشكلة.

في الوقت الحالي ، لدي مكون متصل أود أن أكتب بعض اختبارات الوحدة له ، ولكن هناك مكون متصل متداخل داخل المكون الأول ويبدو أنني انتهيت من حظر الدعائم أو الحالة (أو في معظم الحالات كليهما) لم يتم تعيين المكون المتداخل.

كنت أتساءل عما إذا كانت هناك طريقة لإيقاف مكون واحد بحيث يمكنني التركيز فقط على المكون الذي أحاول اختباره؟

شكرا لك مقدما

question

التعليق الأكثر فائدة

القلق الذي أثير هنا هو عندما يعرض المكون الذي يتم اختباره ثم مكونات أخرى متصلة ، إما كأبناء مباشرين أو في مكان ما أسفل التسلسل الهرمي. إذا قمت بإجراء تصيير كامل عبر mount() ، فستحاول المكونات المرتبطة التابعة تمامًا الوصول إلى this.context.store ولن تجدها. لذلك ، فإن الخيارات الرئيسية هي إما التأكد من عدم عرض المتحدرين من خلال إجراء اختبار ضحل ، أو جعل المتجر متاحًا في السياق.

ال 21 كومينتر

شيئان:

1) هذا هو الاستخدام وربما ينبغي أن يذهب إلى مكدس تجاوز.

2) جزء من ملاءمة استخدام مكون الترتيب الأعلى connect
هو أنه يمكنك فقط كتابة اختبار وحدة لمكون unconnected و
ثق في الإعادة للتعامل مع الأنابيب الموجودة في المتجر كما ينبغي

إذا لم يكن من الواضح ما أعنيه بذلك ، فيمكنني نشر مثال.

فكرتان:

  • طريقة واحدة للتركيز على مكون واحد فقط هي استخدام عرض "سطحي" ، والذي لا يجعل أي توابع
  • يمكنك أيضًا تقديم <Provider store={testStore}><ConnectedComponent /></Provider> في اختباراتك

لمعلوماتك ، لدي روابط لعدد من المقالات حول اختبار React / Redux هنا ، والتي قد تجدها مفيدة: https://github.com/markerikson/react-redux-links/blob/master/react-redux-testing. م .

ونعم ، كسؤال للاستخدام ، من الأفضل طرح هذا السؤال في مكان آخر.

شكرًا markerikson ، أنا أحاول حاليًا الطريقة الأخيرة التي ذكرتها ولكني

ما زلت أعتقد أنك إذا كنت ترغب في اختبار ذلك المكون مرة واحدة في عزلة ، فمن الأفضل أن تفعل شيئًا مثل

// Component.js
import React from 'react'

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

// ...

export default connect(mapStateToProps, mapDispatchToProps)(Component)

ثم يمكنك فقط إحضار المكون قبل توصيله في الاختبار مثل

// 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} />)
})

إذا كان ما تريد حقًا اختباره هو أن التغييرات التي تم إجراؤها على متجرك يتم نشرها بشكل صحيح ، فمن المنطقي تقديم Provider وهذا مختلف ، على الرغم من أنني لست متأكدًا مما إذا كنت تحصل على قيمة كبيرة من اختبار ذلك.

القلق الذي أثير هنا هو عندما يعرض المكون الذي يتم اختباره ثم مكونات أخرى متصلة ، إما كأبناء مباشرين أو في مكان ما أسفل التسلسل الهرمي. إذا قمت بإجراء تصيير كامل عبر mount() ، فستحاول المكونات المرتبطة التابعة تمامًا الوصول إلى this.context.store ولن تجدها. لذلك ، فإن الخيارات الرئيسية هي إما التأكد من عدم عرض المتحدرين من خلال إجراء اختبار ضحل ، أو جعل المتجر متاحًا في السياق.

markerikson آه حسنًا ، لقد أعدت القراءة فقط اعتقدت أن OP كان يحاول اختبار المكون الداخلي بدلاً من الخارجي. 👍

شكرا لمساعدتكم حتى الآن يا شباب ،

المشكلة الرئيسية التي أواجهها في الوقت الحالي هي أنه لا يمكنني تمرير الحالة إلى المكون المتصل المتداخل ، وبالتالي لا يتم تقديم العرض بشكل صحيح.

حالة الاستهزاء بشكل عام هي شيء كنت أعاني منه كثيرًا في الواقع ولم أتمكن من العثور على أي شيء نهائي حول هذا الموضوع ، لذلك سيكون من الرائع أن يعطيني أحدهم فكرة عن كيفية تمرير حالة السخرية إلى متداخل متصل أو مكون ذو ترتيب أعلى؟

TIA

StlthyLee : ماذا تقصد ب "تمرير الدولة"؟ إذا قدمت <Provider store={store}><ComponentWithConnectedDescendants /></Provider> في اختبارك ، فيجب أن يتعامل ذلك مع الأمور.

هذه هي المشكلة ، مجرد القيام بذلك هو عدم معالجته بشكل صحيح ، يتطلب المكون المتداخل بداخله معلمة محددة من حالة التطبيق والتي لا تحصل عليها في الوقت الحالي لسبب أو لآخر.

ربما يجب على StlthyLee وضع مثال / gist / codepen / repo-link أو شيء ما ، سيستغرق اكتشاف المشكلة وقتًا أقل نظرًا لوجود تضارب في المصطلحات هنا.

Koleok نأمل أن يساعد هذا في التوضيح

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

إذا لم يكن الأمر كذلك ، من فضلك اسأل ، لأن هذا شيء أنا حريص على الوصول إليه ، بعد أن أمضيت 9-10 سنوات الماضية في العمل في عالم Rails ولدي فهم جيد لـ TDD من هذا المنظور ، أحاول الآن الحصول على رأسي حول TDD في عالم React / Redux.

لذلك أعتقد أنني وصلت إلى الجزء السفلي من هذه المشكلة الآن ، لم تكن مشكلة اختبار كبيرة ولكن هناك نسخة وخطأ سابق ارتكبهما عضو آخر في الفريق ، أعتقد أن هذا جزء لا يتجزأ من الاضطرار إلى كتابة اختبارات لملاءمة الكود بدلاً من طرق TDD المفضلة لدي. شكرا لجميع المعلومات الواردة هنا لقد كانت قيمة للغاية

فقط لتقديم خيار آخر يمكن أن يعمل مع بعض حالات الاستخدام ، واجهت مؤخرًا هذه المشكلة مع مكون متصل متداخل. بدلاً من إنشاء متجر وهمي ، قمت بتصدير الإصدارات غير المتصلة لكل مكون لاختبارها على حدة بدون متجر. شيء من هذا القبيل:

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)

لقد فعلت الشيء نفسه مع مكونات الطفل المتداخلة بحيث يمكن اختبار كل منها بشكل مستقل.

ثم أخذت المكون المتصل ذي المستوى الأعلى وقمت بتمرير المكونات المتصلة التابعة كدعائم في وظيفة التطبيق render() . عند اختبار الوحدة لمكون المستوى الأعلى ، بدلاً من تمرير المكونات المتصلة ، أقوم فقط بتمرير مكونات وهمية للتأكد من تقديمها في المكان الصحيح. يمكنك أيضًا تمرير المكونات غير المتصلة.

شيء من هذا القبيل:

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

أنا فعلا أحب فكرتك. لكني أفعل شيئًا كهذا بدلاً من ذلك

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

أعتقد أنك إذا قمت بالفعل باختبار child1 و child2 ، فربما لا داعي لوجودهما في مكون TopLevel الخاص بك.

قمت بتمرير وظيفة أيضًا إذا كنت بحاجة إلى تصيير المكون بشكل مشروط.

أنا لا أتبع جملتك الأخيرة رغم ذلك. ما علاقة الاختبار بتمرير المكونات الفرعية إلى مكون آخر؟

حسنا. على سبيل المثال ، لدي مكون خارجي متصل يستخدم مكونًا داخليًا أو عددًا قليلاً من

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

ومع ذلك ، لدي خطأ في أن مكوناتي الداخلية تحتاج إلى

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

ثم حاولت تمرير حالة وهمية وما إلى ذلك. لقد نجحت. ومع ذلك ، في حالتي ، يجب أن أقوم بالكثير من التهيئة لكل مكون متصل ، بما في ذلك المكالمات غير المتزامنة ، والحالة الأولية وما إلى ذلك ، وكان لدي أخطاء أخرى تتعلق بذلك.

أعتقد أن هذا سيكون مبالغة في القيام بكل التهيئة المناسبة لجميع المكونات الداخلية المتصلة إذا كنت أرغب فقط في اختبار المكون الخارجي.

لذلك قررت نقل جميع المكونات المتصلة وتمريرها كدعامات إلى مكون متصل خارجي. والآن يمكنني استبدالها بعلامة div فارغة عندما أريد الاختبار.

أوه نعم ، كان هذا فقط الدافع الأصلي للنمط. لذا لتعديل الجملة ، فأنت تقصد "أعتقد أنه إذا كنت قد اختبرت بالفعل child1 و child2 ، فلا داعي لتمريرهما إلى مكون TopLevel الخاص بك عند اختبار الوحدة له."

متفق عليه.

StlthyLee ، للأسف واجهت نفس المشكلة عند محاولة اختبار مكون يحتوي على مكونات متصلة متداخلة.

الحل الذي وجدته مفيدًا بشكل خاص هو توصيل المكون المتداخل بمخزن إعادة الإرسال اعتمادًا على بيئة الاختبار.

  • عند إجراء الاختبارات ، قم بإعداد متغير البيئة NODE_ENV="test" . يمكنك التقاط هذا المتغير وجعل المكون يتلقى الخصائص الافتراضية. في مثل هذه الحالة ، يكون المكون غير متصل بالمخزن ، وعند اختبار المكون الرئيسي ، لا تظهر أية مشكلات.
  • في حالات أخرى ، اجعل المكون متصلًا. هذا هو السيناريو الافتراضي عند تشغيل المكون في التطبيق.

أ) أولاً ، ستحتاج إلى وظيفة مساعدة تحدد ما إذا كانت البيئة 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);
  };
}

ب) بعد ذلك ، يجب تطبيق defaultProps() و connect() اعتمادًا على البيئة:

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);

ج) تأكد من أن NODE_ENV هو "test" عند إجراء الاختبارات:

  • عند استخدام mocha CLI ، ضع بادئة لضبط بيئة الاختبار: NODE_ENV='test' mocha app/**/*.test.jsx .
  • في حالة حزمة الويب ، قم بتطبيق مكون إضافي:
plugins: [
    // plugins...
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('test')
      },
    })
]

markerikson شكرا على الاقتراحين. جربت الثانية ولفت المكون الخاص بي للاختبار مع مقدم الخدمة. في هذه الحالة ، يعمل mapStateToProps بشكل صحيح ولكن لا أحصل على النتائج المتوقعة مع mapDispatchToProps . هنا يعرض المكون الذي يتم اختباره المكونات الأخرى المتصلة.

يبدو mapDispatchToProps الخاص بي مثل

/* <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),
});

هنا قيمة الإجراءات التي تم إرجاعها هي 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;

أدرك أن هذا موضوع قديم إلى حد ما ، لكنني وجدت أن هذا يمثل مشكلة مزعجة إلى حد ما ، واعتقدت أن هذا الحل قد يساعد شخصًا يتعثر في هذا الموضوع (مثلي).

كما أنني لم أكن في الواقع اختبار أي شيء متعلق يبعث من جديد، وكان الحل بسيط وجدت أن يسخر وظيفة الاتصال إلى العودة فقط مكون دون تغيير التي تم تمريرها إلى ذلك.

على سبيل المثال ، في التطبيق الخاص بك قد يكون لديك مكون:


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

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

ثم في بداية ملف الاختبار الخاص بك يمكنك وضع وظيفة اتصال سخرية لإيقاف تفاعل Redux مع المكون الخاص بك:

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

يجب وضع هذا الاستهزاء في كل ملف اختبار يقوم بتوصيل طفل متصل.

scottbanyard كيف تمرر الدعائم إلى المكون التابع؟

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

mickeyreiss-visor picture mickeyreiss-visor  ·  3تعليقات

dmitry-zaets picture dmitry-zaets  ·  3تعليقات

ms88privat picture ms88privat  ·  3تعليقات

ilearnio picture ilearnio  ·  3تعليقات

captbaritone picture captbaritone  ·  3تعليقات