Redux: لا يمكن الإشارة إلى الحاويات الملفوفة في مزود أو عن طريق الاتصال بالإنزيم

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

لا يمكنني الإشارة إلى أي شيء ملفوف في <Provider> و 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);

لقد اتبعت: https://github.com/reactjs/redux/issues/1481 للأمثلة التي تمت فيها كتابة الاختبارات باستخدام الإنزيم ، ومع ذلك ، لم يتم اختبار الحاويات فيها أبدًا. لذلك لا أعرف ما إذا كان ينبغي علي / يمكنني اختبار الحاويات الذكية؟

fshowalter أي أفكار؟

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

على الرغم من أنه لا يرتبط مباشرة بـ Redux. لقد قمت بحل هذا عن طريق استدعاء shallow() على الحاوية مرة أخرى. إنه يجعل المكون الداخلي مع حالة المخزن التي تم تمريرها إليه.

مثال:

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

أتمنى أن يساعدك هذا

ال 51 كومينتر

لا يعد أي connect() ولا Provider جزءًا من هذه المكتبة. من الأسهل مناقشة مثل هذه المشكلات وتتبعها عند تقديمها في المستودع المناسب بدلاً من ذلك: https://github.com/reactjs/react-redux.

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

فكرتان هنا:

أولاً ، لاحظ أن مكون الغلاف الذي تم إنشاؤه بواسطة connect() يبحث بالفعل عن props.store قبل أن يبحث عن context.store ، لذلك إذا كنت تشعر بالفعل أنك بحاجة إلى اختبار مكون متصل ، يجب أن تكون قادرًا على تقديم <ConnectedComponent store={myTestStore} /> دون الحاجة إلى القلق بشأن المزود أو السياق أو أي شيء.

السؤال الثاني هو ما إذا كنت تحتاج حقًا إلى القلق بشأن اختبار المكون المتصل بالكامل فعليًا. الحجة التي رأيتها هي أنه إذا كان بإمكانك اختبار المكون "العادي" الخاص بك باستخدام دعائم محددة ، ويمكنك اختبار تنفيذ mapStateToProps الخاص بك ، يمكنك أن تفترض بأمان أن رد فعل- redux سيجمعهم معًا بشكل صحيح ، و لا تحتاج إلى اختبار الإصدار المتصل نفسه بالفعل.

gaearon أنت على حق ، آسف. لم أكن أعرف ما إذا كان سيتم رفع هذا في رد فعل أو إنزيم الريبو.

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

سأفكر في هذا أكثر وأثير مشكلة في الريبو ذي الصلة إذا كنت في حاجة إليها. شكرا للمساعدة.

mordra : عندما تقول "تم استدعاء المرسل الصحيح" ، هل تعني في الواقع "منشئ الإجراء الصحيح"؟

يمكنك أن تفعل شيئًا كهذا (ربما يكون بناء الجملة معطلاً ، لكن يجب أن تفهم الفكرة):

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;

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

أيضًا ، وفقًا لتعليقي السابق: يجب أن تكون قادرًا على اختبار mapStateToProps و mapDispatchToProps بشكل منفصل ، وتشعر بالأمان لأن React-Redux سوف يتصل بهم بشكل مناسب ، دون الحاجة إلى محاولة اختبار الإصدار المتصل نفسها للتحقق من أن هذا هو الحال.

markerikson لا يمكنني فعل ما تقترحه لأن PlainComponent لا أعرف شيئًا عن الإجراءات أو منشئو الإجراءات. لا أعرف ما إذا كانت هذه هي الطريقة الصحيحة للقيام بذلك ، ولكن إذا نظرت إلى:
https://github.com/mordra/cotwmtor/blob/master/client/charCreation/charCreation.jsx
ستلاحظ أن mapDispatchToProps الخاص بي يحتوي على كل المنطق:

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

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

آه. هذا يساعدني على فهم المزيد.

لذلك مع التحذير بأنني في الواقع لدي القليل جدًا من الخبرة العملية في كتابة الاختبارات ، فكرتان:

  • ما لديك بالفعل هو صانعي الإجراءات ، لم يتم تعريفهم بشكل منفصل لأنك تريد الوصول إلى dispatch . هذا ليس من السهل اختباره في حد ذاته. ربما تريد أن تكون قادرًا على اختبار سلوكهم أيضًا ، وللقيام بذلك ، قد ترغب في تعريفهم كوظائف خاصة بهم خارج mapDispatch .
  • كل هؤلاء يبدون كمرشحين جيدين جدًا للاستخدام مع redux-thunk ، مما سيجعل من السهل تحديد هذه الوظائف بمفردهم والسماح لهم بالوصول إلى dispatch .
  • يعرف _does_ الخاص بـ PlainComponent "الإجراءات" ، أو على الأقل عمليات الاسترجاعات في هذه الحالة ، حيث تقوم بتمريرها ، وفي مكان ما في المكون الخاص بك تقوم بتشغيل this.props.onSetDifficulty("HARD") أو شيء من هذا القبيل. عندما اقترحت تمرير جواسيس للقيام بأفعال ، كان هذا هو الشيء الذي كنت أقترح استبداله. لذلك ، إذا قمت بتمرير تجسس onSetDifficulty ، يمكنك التحقق من أن المكون الخاص بك يطلق عليه ، وتمرير قيمة مقبولة لمستوى الصعوبة.

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

أيضًا ، من وجهة نظر المكون: في النهاية ليس هناك قلق فعليًا بشأن ما إذا كان قد تم إرسال {action: CHANGE_NAME, name : "Fred"} أم لا. كل ما تعرفه هو أنه يسمى this.props.onChangeName("Fred") ، و _ هذا ما يجب أن تحاول اختباره - أنه دعا رد نداء prop بالطريقة الصحيحة.

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

ومع ذلك ، فإنني أميل إلى تجنب mapDispatchToProps لصالح mapActionCreatorsToProps الذي يرسم خرائط لمنشئي الحركة المشكَّلين بالفعل والذين يمكنني اختبارهم بسهولة بمعزل عن الآخرين. في هذه الحالة ، أختبر للتو أن كل الدعائم الخاصة بي محددة للحماية من أخطاء الاستيراد المطبعية.

لاحظ أخيرًا أنه يمكنك استخدام العرض العادي (غير الضحل). ستحتاج إما إلى jsdom أو متصفح حقيقي لذلك ، ولكن بعد ذلك يمكنك عرض أي مستوى بعمق.

لم أكن أعرف ما إذا كان سيتم رفع هذا في رد فعل أو إنزيم الريبو

عذرًا ، لم أقصد React أو Enzyme repos. قصدت React Redux وهي المكتبة التي تستخدمها.

شكرًا على المساعدة يا رفاق ، هذه معلومات كثيرة بالنسبة لي لأذهب وأستوعبها. markeriksonfshowalter لقد بذلت المزيد من البحث حولك وسأخذ اقتراحك بتصدير mapState / Dispatch واختبارهما بشكل منفصل ، فمن المنطقي اختبار عمليات الاسترجاعات التي تثير الإجراءات المتوقعة.

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

لست متأكدًا من المشكلات التي تشير إليها. يمكنك استخدام العرض الضحل مع المكونات الوظيفية على ما يرام. يعمل العرض الضحل على مستوى واحد فقط عميقًا (بغض النظر عن كيفية تعريف المكون). هذه هي ميزتها الرئيسية - فهي لا تتكرر لذا تظل اختبارات المكونات مستقلة. إذا كانت لديك مشكلات محددة مع العرض الضحل الذي يمكنك إعادة إنتاجه ، فيرجى تقديم أخطاء في React repo. شكرا!

gaearon : للتوضيح ، إذا قمت بذلك:

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

هل سيتم تقديم "ب" أم لا؟ لأنني أعتقد أن هذا هو السؤال الأصلي - محاولة تقديم <Provider> حول مكون متصل من أجل اختبار الاتصال.

على الرغم من أنه لا يرتبط مباشرة بـ Redux. لقد قمت بحل هذا عن طريق استدعاء shallow() على الحاوية مرة أخرى. إنه يجعل المكون الداخلي مع حالة المخزن التي تم تمريرها إليه.

مثال:

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

أتمنى أن يساعدك هذا

هل ينبغي لنا أن نجعل طريقة أكثر ودودًا للقيام بذلك من خلال وحدة مخصصة ربما؟

تحرير: لذلك يمكننا أن نعلن الاتصال كطريقة واحدة للإنزيم ، مثل component = shallowWithConnect()

@ ev1stensberg هذه فكرة رائعة. هل قررنا ما إذا كان هذا هو النهج و / أو بدأ العمل على هذا؟ إذا لم يكن الأمر كذلك ، فأنا أحب المساهمة.

بالنسبة لأي شخص يتعثر في هذه المشكلة في المستقبل ، إليك الطريقة التي تناسبني: ما عليك سوى اختبار المكون العادي بمفرده. قم بتصدير كل من تعريف المكون كتصدير مسمى والمكون المتصل (للاستخدام في تطبيقك) كتصدير افتراضي. العودة إلى الكود من السؤال الأصلي:

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

ثم فقط افعل

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

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

حسنًا ، أول شيء يجب ملاحظته هنا هو فلسفة :

  1. ابلاغ الحالة من المخزن الى أ
  2. تعديل الحالة من خلال إيفاد الإجراءات على أساس الأحداث.

إذا كنت بخير ، فما عليك سوى اختبار شيئين:

  1. هل يستقبل المكون الخاص بك الدعائم الصحيحة التي تم إنشاؤها من الحالة (يمكن أن تكون عبر المحددات)؟
  2. هو المكون الخاص بك إرسال الإجراءات الصحيحة؟

لذلك هذا هو أسلوبي

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 ماذا تقصد ب:

يمكنك اختبار تنفيذ mapStateToProps الخاص بك

لنفترض أنك تقوم بتصدير الحاوية المتصلة عبر تصدير ES6. لن تتمكن من الوصول إلى الخريطة الخاصة. هل كنت تتحدث عن طريقة أخرى أم يمكنك أن تكون محددًا؟

تضمين التغريدة

يمكنك التحقق من صحة جميع أسماء الخصائص

لذلك ، إذا قمت بكشف mapStateToProps الخاصة بك وجعلتها عامة ، فقل أنك تقوم بعرض ContainerComponent من الاختبار الخاص بك بما في ذلك إرسال متجر وهمي على سبيل المثال وإرسال دعامة حالة ، ثم يتلقى mapStateToProps هذه الحالة ، ويقوم بتعيينها إلى عنصر. ولكن كيف يمكنك اختباره من تلك النقطة؟ سوف يقوم Connect باستدعاء mapStateToProps وبالتالي دمج الدعائم ولكن أين هو التماس وأي نقطة هي التماس في الكود أثناء هذه العملية / التدفق حيث يمكنك اعتراض الدعائم التي يتم تعيينها على المكون التقديمي؟ أعتقد أن الضحلة المزدوجة مثل @ guatedude2 قد تكون إحدى الطرق للتحقق من الدعائم المعينة بدون أي طبقات ...

ايضا على ضحلك. لذا فإن مكون الاتصال () يمر مرة أخرى ، غلاف صحيح؟ وهذا الغلاف ما الذي يلف عنصر العرض؟

granmoe ، هل يمكنك شرح هذا بمزيد من التفصيل فيما تعنيه بالضبط:

إذا كان لديك أيضًا تغطية للوظائف التي تقوم بتمريرها إلى الاتصال ، فيجب أن يكون لديك تغطية كاملة للمكون الخاص بك ككل.

أنا أيضًا مع tugorez من حيث _what_ أريد اختبارها ولماذا.

markerikson مثال جميل جدا مع الجاسوس. إنه شيء واحد يمكنك اختباره ، لكنك ستحتاج إلى مزيد من المؤكدين لاختبار "وحدة السلوك". مجرد اختبار استدعاء إجراء تجسس من mapDispatchToProps لا يخبرنا حقًا القصة الكاملة حول مكون الحاوية. لا تؤكد على النتيجة المتوقعة بناءً على منطق معالج الحاوية.

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

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

2) يعمل منطق معالج الإرسال (السلوك) في الحاوية.

dschinkel :

الممارسة النموذجية لتعريف المكونات المتصلة بـ Redux هي export default connect()(MyComponent) ، وأيضًا export MyComponent كتصدير مسمى. وبالمثل ، نظرًا لأن mapState مجرد وظيفة ، يمكنك أيضًا export const mapState = () => {} ، ثم استيرادها واختبارها بشكل منفصل.

فيما يتعلق باختبار "مكون الحاوية" مقابل "المكون التقديمي": الفكرة العامة هنا هي أنك ربما لا تحتاج إلى القلق بشأن اختبار الحاوية في أغلب الأحيان ، حيث تكون "الحاوية" === "ناتج connect ". لدى React-Redux بالفعل مجموعة اختبار كاملة لكيفية تصرف ذلك ، ولا يوجد سبب لتكرار هذا الجهد. نحن نعلم أنه يتعامل بشكل صحيح مع الاشتراك في المتجر ، والاتصال بـ mapState و mapDispatch ، وتمرير الدعائم إلى المكون المغلف.

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

(أيضًا ، FWIW ، أدرك أنك خبير تمامًا في الاختبار وأنا لست كذلك ، ولكن يبدو أنك تشعر بجنون العظمة قليلاً بشأن الأشياء :) إذا كنت قلقًا بشأن وجود mapState كسر عرضيًا ، ثم اكتب اختبارات له وامضي قدمًا.)

الآن ، إذا كان المكون الملفوف الخاص بك يعرض مكونات أخرى متصلة بداخله ، وخاصة إذا كنت تقوم بالعرض الكامل بدلاً من العرض الضحل ، فقد يكون من الأسهل اختبار الأشياء عن طريق إجراء const wrapper = mount(<Provider store={testStore}><MyConnectedComponent /></Provider> . ولكن ، بشكل عام ، ما أراه هو أنه في معظم الأوقات _يجب_ أن تكون قادرًا على اختبار وظيفتك mapState والمكون "العادي" بشكل منفصل ، ويجب ألا تحاول اختبار الإصدار المتصل فقط للتحقق أن ناتج mapState يتم تمريره إلى المكون العادي.

كمرجع ، قد ترغب في إلقاء نظرة على الإصدار المصغر من connect الذي كتبه دان لبعض الوقت لتوضيح ما يفعله داخليًا: https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e .

نعم ، أعني إذا قمت بتصدير mapStateToProps و dispatchStateToProps ، فسيكون ذلك أفضل. لا أريد اختبار عمل الاتصال () (المتعاون في الاختبار) ، بالتأكيد لا. لذلك ستكون هذه طريقة واحدة واضحة للقيام بذلك ، قم بتصدير هاتين الطريقتين:

example-container-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');
    });
});

حاوية

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

عرض تقديمي

import React, { Component } from 'react';

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

ثم مرة أخرى ، يمكنك أيضًا القيام بشيء مثل هذا من خلال العرض _shallow_ والاستمرار في الغوص في المكون الفرعي:

example-container-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);
    });

حاوية

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

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

export default connect(mapStateToProps)(Example);

عرض تقديمي

import React, { Component } from 'react';

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

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

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

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

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

أدرك أنك الخبير تمامًا في الاختبار وأنا لست كذلك ، لكن يبدو أنك تشعر بجنون العظمة قليلاً بشأن الأشياء :)

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

من المهم اختبار السلوك فقط وعدم اختبار المتعاونين (ربط () نفسه) كما قلت. من المهم معرفة ذلك لأول مرة واختبار البئر. من المهم التأكد من أنك لا تختبر المتعاونين وهذا يتطلب أحيانًا مناقشة بين زملائك المطورين.

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

جديد React Test Repo قادم ...
في الواقع ، سأشارك ريبو قريبًا يعرض أنماطًا ومجموعات وأساليب مختلفة لاختبار رد الفعل والإعادة. أعتقد أنه سيساعد الناس لأنني لست الوحيد الذي يفكر في هذا الأمر. مرارًا وتكرارًا ترى أشخاصًا يطرحون نفس الأسئلة حول اختبار حاوية إعادة الإرسال والمكونات الأخرى. مستندات Redux ولا تقوم مستندات رد الفعل والإعادة بإنصاف في ذلك ، فهي تفتقر إلى الوثائق في منطقة الاختبار IMO. إنه يخدش السطح فقط ، ونحن بحاجة إلى ريبو لطيف يُظهر أنماطًا مختلفة حول الاقتراب من اختبار React ، لذا سأضع ذلك قريبًا.

If anyone would like to contribute to that repo, please get a hold of me حتى أتمكن من إضافتك كمتعاون في ذلك. أود أن أجعل الناس يضيفون أمثلة. أرغب في رؤية أمثلة باستخدام React Test Utils + mocha مباشرة مع Enzyme و Ava و Jest والشريط وما إلى ذلك.

الخلاصة :

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

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

markerikson شكرا على المدخلات الخاصة بك أحب التحدث اختبار. رجل الاشياء الجيدة. لم أكن أشكك في خبرتك ، فقط لأنني لم أحاول بالفعل اختبار mapStateToProps مباشرة بنفسي! أنت لا تفعل شيئًا سيئًا لكونك غير مختبِر ؛). أنا ببساطة جديدة على إعادة القراءة ، وبسيط على هذا النحو ، سيكون لدي أسئلة لا "تلمس السطح" فقط. يجب أن نناقش الاختبار على مستوى أدنى حتى يفهم الناس ما يفعلونه ، وكيف يمكنهم القيام به ، وما إذا كانوا يستفيدون من نهجهم. للقيام بذلك ، عليك أن تفهم الاتصال ، والتفاعل وإعادة الإرسال ، والمزود بمستوى أدنى ... حتى تعرف كيفية "اختبار السلوك فقط". لم أر الكثير من الأشخاص يصدرون mapStateToProps وهو أمر مثير للاهتمام بالنسبة لي أيضًا ... وهذا يجعلني أتساءل لماذا لا.

WeDoTDD.com
راجع للشغل بالنسبة لأي شخص مهتم ، بدأت WeDoTDD.com العام الماضي (مكتوبًا في React راجع للشغل). إذا كان فريق معين (جميع المطورين في فريق معين) TDD ، أو شركتك بأكملها (بعض الشركات لديها مطورون حيث جميع TDD خاصة الشركات الاستشارية) ، يرجى التواصل معي على slack.wedotdd.com

تحديث.

بعد اللعب أكثر باختبار المكونات المتصلة والتفكير في الأمور التي تعمل بشكل رائع بالنسبة لي ، أتفق الآن بنسبة 100٪ مع بيان markerikson هنا:

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

لم أر أي حاجة لإدخال <Provider /> في اختباراتي ويبدو أنك تختبر متعاونًا إذا فعلت ذلك عندما لا تكون النقطة هي اختبار أن <Provider /> يعمل. يجب أن تختبر أن السلوك في المكون الخاص بك يعمل ولا يهم ما إذا كانت الحاوية الخاصة بك تحصل على المتجر عبر السياق. ما عليك سوى تمرير المتجر كعنصر خاص .. الاتصال يحتاج فقط إلى طريقة للوصول إلى المتجر ، ولا يهم كيف (السياق أو الدعائم) ... تخلص من المزود تمامًا في اختباراتك.

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

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

سعيد لسماع أنك نجحت في تنفيذ الأمور. (FWIW ، كانت تعليقاتي إلى حد كبير ردًا على أسئلتك حول "اللحامات" و "الحفر في الدعائم" ، والتي بدت حقًا وكأنها تذهب إلى مستويات غير ضرورية من التفاصيل مقابل فائدة قليلة أو معدومة.)

سيكون استخدام <Provider> / Context _is_ ضروريًا إذا كنت تقوم بإجراء عرض كامل لمكون يعرض مكونات أخرى متصلة ، حيث سيبحث هؤلاء الأطفال المتصلون عن المتجر في السياق.

إذا كنت قد تلقيت اقتراحات لتحسين مستندات Redux الحالية عند الاختبار ، فقم بتقديم PR بكل الوسائل.

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

على اي حال الاشياء الجيدة ، شكرا!

حسنًا ، أقول فقط أنك إذا _do_ تستخدم mount للتحقق من دورة حياة المكون المعني ، وأن هذا المكون يعرض مكونات أخرى متصلة ، فإنك _ سوف تحتاج_ إلى أن يكون المتجر متاحًا في سياق تلك المكونات المتداخلة لتجنب الأخطاء عندما يتم تقديم المكون قيد الاختبار.

(تحرير: من الواضح أنني أكرر نفسي نوعًا ما ، آسف - مخاطر الإجابة على الأسئلة في أماكن متعددة دون الرجوع دائمًا عبر سلسلة رسائل ، أو حتى التمرير لأعلى قليلاً).

آه حسنا فهمت! نعم لم أفكر في ذلك ...

شكرًا لجميع التعليقات هنا ، markerikson وdschinkel! إنها مفيدة للغاية. لقد قررت الآن تصدير المكون غير المتصل واختباره كما أفعل مع أي مكون آخر ، وكذلك اختبار mapStateToProps و mapDispatchToProps بشكل منفصل. ومع ذلك ، هناك حالة واحدة لم يتم اكتشافها في هذه الاختبارات ، وذلك عندما لا يتم تمرير map(State/Dispatch)ToProps فعليًا في المكالمة إلى connect . مثال:

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`.

من المحتمل ألا يحدث هذا في الممارسة العملية ، ولكنه قد يحدث ، خاصة وأن linters لن يبلغ mapStateToProps كمتغير غير مستخدم منذ أن يتم تصديره.

@ danny-andrews هل يمكنك أن تكون أكثر تحديدًا حول كيفية اختبار وظيفة mapDispatchToProps الخاصة بك؟

leizard

لم أعد أختبر mapDispatchToProps أو mapStateToProps مباشرة. لقد غيرت رأيي منذ هذا الموضوع وأنصح بعدم القيام بذلك.

اختبارها أيضًا ستواجه مشكلة @ danny-andrews ولكن المشكلة تتعلق أكثر بالاختبارات الجيدة ومحاولة الكشف عن هاتين الوظيفتين ليست فكرة جيدة. انتهى الاختبار. يجب عليك اختبار هاتين الطريقتين بشكل غير مباشر عن طريق اختبار السلوك أو عن طريق اختبار الدعائم من خلال الحاوية الضحلة بدلاً من ذلك. لقد وجدت أنه لا يوجد سبب لمحاولة الكشف عن هذه الأساليب الخاصة وأدركت الآن أنها كانت أيضًا ممارسة اختبار سيئة لمحاولة القيام بذلك.

لذلك أنا لا أتفق مع markerikson ، اختبر المكون المتصل.

العثور على API / العقد المناسب للاختبار وعدم المبالغة في الاختبار ، هذا هو المفتاح الصغير :).

تضمين التغريدة

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

هل تقترح أنه يجب عليك التخلي عن تصدير المكون غير المتصل تمامًا واختبار المكون المتصل فقط؟ أعتقد أن هذا "انتهى بالاختبار". يؤدي هذا إلى ربط اختباراتك دون داعٍ بتفاصيل تنفيذ المكون الذي تختبره. نظرًا لأن وظيفة المكون غير المتصل (إذا كان يحتوي على أي منها ، فهذه علبة أخرى كاملة من الديدان) غير مرتبطة تمامًا بالمكان الذي يتلقى منه الدعائم. ربما ترغب في تحويل هذا المكون إلى عنصر عرضي بحت في المستقبل. إذا اختبرت المكون المتصل ، فسيتعين عليك تغيير جميع اختباراتك لعدم إدخال متجر بعد الآن ولكن تمرير الدعائم مباشرة. إذا قمت باختبار المكون غير المتصل ، فلن تضطر إلى فعل أي شيء باستثناء التخلص من الاختبارات الخاصة بالمكونات المتصلة (المزيد حول ذلك أدناه).

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

  1. تصدير المكون غير المتصل والمكون المتصل
  2. لا تقم بتصدير mapStateToProps أو mapDispatchToProps
  3. اختبر منطق المكون بالكامل عبر المكون غير المتصل
  4. اختبر المكون المتصل فقط عند اختبار التفاعل مع الإعادة (يتم تمرير دعائم البيانات من المكان المناسب في المتجر / الدعائم العملية لمنشئ الإجراء المناسب ، وما إلى ذلك).

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

طريقة 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);
});

أسلوب جديد

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

نعم هذا ما أقوله أنا لا أقوم بتصدير واختبار mapStateToProps ، لقد انتهى الاختبار. اختبر من خلال API . API هنا هو الحاوية .

@ danny-andrews

اختبر منطق المكون بالكامل عبر المكون غير المتصل

هل يمكنك موقع بعض الأمثلة؟

أعني نفس الشيء الذي قلته: "يجب أن تختبر هاتين الطريقتين بشكل غير مباشر عن طريق اختبار السلوك"

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

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

يفضل بعض الأشخاص تقسيمها بالكامل وجعل "الحاويات" الخاصة بهم مجرد مكالمات إلى connect والتي تغلف مكون عرض تقديمي بحت. في هذه الحالة ، يختفي الرقم 1 و 3 في القائمة والاختبارات الوحيدة التي تحتاج إلى كتابتها هي الاختبارات التي تتحقق من التفاعل مع redux (رقم 4).

أخطط لكتابة منشور مدونة ضخم حول هذا الموضوع. أنا مشغول في الوقت الحالي في الكود لكنني سأرد لاحقًا

بعد قراءة هذا الموضوع عدة مرات ، توصلت إلى استنتاج مفاده أن هناك 3 طرق موصى بها:

  1. قم بتصدير mapDispatchToProps و mapStateToProps واختبرهما بشكل منفصل
  2. عرض عنصر الحاوية الضحلة واختبار الأسلاك صحيحة
  3. استخدم المحددات بدلاً من mapStateToProps ومنشئي الإجراءات بدلاً من mapDispatchToProps واختبرهم بشكل منفصل ؛ اكتب الاختبارات باستخدام منشئي الإجراء ومخفضات الحركة والمحددات معًا للتأكد من أن التدفق بالكامل يعمل.

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

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

لهذا السبب أميل إلى نهج dschinkel وليس تصدير mapDispatch و mapState ، لأنهما بالنسبة لي تطبيق خاص للمكون المتصل.

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

هل سبق لك أن كتبت منشور المدونة؟ dschinkel أحب قراءته

تضمين التغريدة

نعم ، أتفق معك في أن مجرد تصدير mapDispatch و mapState يبدو أمرًا خاطئًا ، وربما لن أستخدمه بنفسي ، ومع ذلك فهو خيار صالح ، على الرغم من أنه خيار أقل "نقاء" . لقد تركتها هناك كخيار محتمل أيضًا لأن دان أبراموف اقترح هذه التقنية الدقيقة هنا: https://github.com/reduxjs/react-redux/issues/325#issuecomment -199449298.

للإجابة على استفسارك عن "ما يجب فعله بمكون ، والذي يلتف فقط حول مكون آخر حول اتصال" ، أود أن أقول فقط لا تختبره. لا يوجد منطق عمل متعلق بتطبيقك ولا تريد اختبار تنفيذ connect - الذي تم اختباره بالفعل في مكتبة react-redux .

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

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

ucorina آسف على الإزعاج مرة أخرى ، ولكن بعد النوم عليه لفترة من الوقت ، لست متأكدًا من أنني أتفق تمامًا مع الخيار 2 أيضًا. لست متأكدا بالرغم من ذلك. لست متأكدًا أيضًا من فائدة اختبار mapStateToProps على الإطلاق.

هل تعتقد أنه من الجيد أن "الخيار 2" ، بشكل غير مباشر يختبر أيضًا صانعي الإجراء؟ وكذلك اختبار المحددات بشكل غير مباشر؟ هل هذا شيء جيد؟
أنا لست مقتنعًا بذلك بنسبة 100٪ ، لكنني أيضًا محتار بشأن كل المعلومات (الخاطئة) الموجودة هناك. هناك العديد من الأفكار المختلفة للاختبار.

  1. استخدم فقط المحددات داخل mapStateToProps

    • الاستهزاء بهم أثناء اختبارات الحاوية إذا لزم الأمر

    • اختبار المحددات بشكل منفصل.

    • استخدم فقط منشئ الإجراء داخل mapDispatchToProps

    • الاستهزاء بهم أثناء اختبارات الحاوية إذا لزم الأمر.

    • اختبار منشئو الإجراء بشكل منفصل.

    • المكون المتصل ضحل

    • يؤكد أنه يتم تقديمه

    • اختبر أي منطق إضافي يجب إضافته إلى mapStateToProps أو mapDispatchToProps أو mergeProps ولم يتم اختباره بالفعل في مكان آخر. (الإرسالات المشروطة ، المحددات التي تأخذ args من ownProps ، إلخ)



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



بديل:

  • حالة المتجر الوهمي لاختبار الحاوية + المحددات معًا
  • استدعاءات وهمية خارج واجهة برمجة التطبيقات لاختبار الحاوية + الإجراءات معًا

AnaRobynn أعتقد أن نهجucorina الثاني هو النهج الصحيح.

يوحي الكثير مما أسمعه:

  1. يجب عليك كشف mapDispatchToProps ، واختبارها مباشرة
  2. لا تحتاج إلى اختبار مكالمتك للاتصال () لأن الاتصال () قد تم اختباره بالفعل

فيما يتعلق بـ 1 ، لا أعتقد أنه من الممارسات الجيدة كشف الأجزاء الداخلية من أجل الاختبار. اختباراتك (أ): تفترض الآن الهيكل الداخلي ، و (ب): يجب أن تختبر الشيء كيف سيتم استخدامها بالفعل.

بخصوص 2 ، يجب أن تختبر شيئًا ما يستدعي مكتبة خارجية. هذه واحدة من نقاط الضعف في أي تطبيق للكسر. مع أي مكتبة ، لا يزال من الممكن إدخال تغيير كسر في عثرة إصدار ثانوي / تصحيح ، وأنت تريد أن تفشل اختباراتك بسرعة.

هل تعتقد أنه من الجيد أن "الخيار 2" ، بشكل غير مباشر يختبر أيضًا صانعي الإجراء؟ وكذلك اختبار المحددات بشكل غير مباشر؟ هل هذا شيء جيد؟

نعم ، تعد تغطية الاختبار الزائدة عن الحاجة أمرًا جيدًا.

تضمين التغريدة
أعتقد أنني أصبحت مرتبطًا قليلاً باختبار تطبيقات React. معتقداتي وأفكاري الحالية حول هذا الموضوع:

  1. لا تكشف عن mapStateToProps و mapDispatchToProps إذا لم تكن بحاجة إلى هذه التعيينات في مكونات متعددة. تصديرها فقط للاختبار هو مضاد للنمط.
  2. أرى حاليًا الحاويات كمتعاونين (الوظائف التي تفوض الوظائف الأخرى وتلك الوظائف الأخرى تؤدي المنطق الحقيقي).
    ماذا يعني ذلك؟
  3. الحاويات لا تؤدي أي منطق
  4. منطق اختيار الحالة هو لوظائف المحدد (والتي يمكن اختبارها فقط على أساس الوحدة)
  5. يتم إخفاء منطق الإجراءات في صانعي الإجراء (والذي قد يعتمد على ما إذا كنت تستخدم redux-thunk أم لا متعاونين بأنفسهم)
  6. العرض الذي يتم عرضه هو وظيفة أخرى (من المحتمل أن يكون لها وظيفة متعاون أخرى أو تعرض الأشياء فقط

=> إعداد الاختبار له في الواقع واضح جدًا عند استخدام مكتبات الاختبار المناسبة (أوصي testdouble.js).
=> اسخر من المحدد والإجراءات الخاصة بك عبر testdouble.js (باستخدام td.when() )
=> عرض مكوّن الحاوية بشكل سطحي ، dive() مرة واحدة للوصول إلى طرق مكوّن العرض
=> إعطاء مجموعة القواعد بواسطة td.when() تأكيد ما إذا كان المكون يتصرف بشكل صحيح

لا حاجة لحقن بعض مكتبات fakeStore ، فلا بأس من إيقاف المتجر.

مثال:

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

أفكار؟

AnaRobynn أتفق بشدة مع النقطتين 1 و 2!

عادة ما تكون حاوياتي مستقيمة جدًا للأمام:

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

لذلك أود أن

  1. تجسس على جميع المحددات ومنشئ الإجراءات
  2. test getPlaceholders تم استدعاء الجاسوس بمعرف المستخدم الصحيح
  3. تأكيد أن SampleComponent تم تقديمه باستخدام العناصر الصحيحة
  4. قم باستدعاء الخاصية onMount من SampleComponent وتأكد من أنها ترسل الإجراء الساخر عند shouldDoStuff===true
  • أشياء مثل العرض الشرطي للأشياء اعتمادًا على الدعائم التي سأختبرها في اختبار منفصل مقابل SampleComponent
  • أفضل أيضًا استخدام redux-mock-store للسخرية من المتجر مثل configureMockStore([thunk])() لكن أعتقد أنه من الجيد عدم القيام بذلك ...

TL ؛ DR I في الأساس تم استدعاء محددات الاختبار فقط باستخدام الوسائط الصحيحة ، وتم إرسال الإجراءات التي تم الاستهزاء بها بنجاح (إذا كانت تعتمد على أي منطق) وأن المكون الفرعي يتم تقديمه باستخدام الدعائم الصحيحة

تضمين التغريدة

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

dougbacelar هل يمكنك إعطاء عينة من كود الاختبار لاختبار المكون الذي أظهرته. هل تستخدم الانزيم؟ شيء آخر؟ بارز؟ تتعدد؟

تضمين التغريدة

أنا أعارض بشدة اختبار المكتبات الخارجية ، وأفضل الاستهزاء بكل شيء واختبار الأشياء بمعزل عن غيرها.

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

السبب في عدم رغبتي في اختبار الحاويات + المحددات + منشئي الإجراءات معًا هو أنه سيتعين عليك إعادة اختبار نفس المحددات ومنشئي الإجراءات لكل حاوية أخرى تعيد استخدامها. في هذه المرحلة ، من الأفضل إضافة المزيد من حالات الاختبار إلى اختبارات الوحدة نفسها.

نعم ، اختبر محدداتك ومنشئو الإجراءات بشكل مستقل. اختبرها بدقة مع الكثير من المدخلات المتوقعة.

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

إليكم ما أعنيه ...

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

هنا ، هذا لا يختبر جميع المعلمات المختلفة التي يمكن أن تحصل عليها selectColors و saveColor ؛ إنه يختبر أنه عند إنشاء <ColorButtons /> ، فإنه يحصل على الخصائص التي نتوقع الحصول عليها. اختبر هؤلاء في اختبار آخر تمامًا.

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'])
  })
})

أنا أعارض بشدة اختبار المكتبات الخارجية ، وأفضل الاستهزاء بكل شيء واختبار الأشياء بمعزل عن غيرها.

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

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

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

إليكم ما أعنيه ...

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

هذا الخيط هو إعادة صياغة النقاط التي تم إجراؤها في https://martinfowler.com/articles/mocksArentStubs.html ، Classic and Mockist Testing

متفق. قفل.

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