الفرق بين المراجعتين ل"React/test utils"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(تحديث)
 
(3 مراجعات متوسطة بواسطة مستخدمين اثنين آخرين غير معروضة)
سطر 1: سطر 1:
<noinclude>{{DISPLAYTITLE:أدوات الاختبار}}</noinclude>
+
<noinclude>{{DISPLAYTITLE:أدوات الاختبار في React}}</noinclude>
 
 
 
== استيراد الأدوات ==
 
== استيراد الأدوات ==
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
import ReactTestUtils from 'react-dom/test-utils'; // ES6
 
import ReactTestUtils from 'react-dom/test-utils'; // ES6
 
var ReactTestUtils = require('react-dom/test-utils'); // ES5 with npm
 
var ReactTestUtils = require('react-dom/test-utils'); // ES5 with npm
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
== لمحة عامة ==
 
== لمحة عامة ==
تزيد أدوات الاختبار <code>ReactTestUtils</code> من سهولة اختبار مكوّنات React في إطار عمل الاختبار الذي تريده. يستخدم Facebook الأداة [https://facebook.github.io/jest/ Jest] لاختبار JavaScript بسهولة. تعلم كيفية البدء مع Jest عبر [http://facebook.github.io/jest/docs/en/tutorial-react.html#content درس React] في موقع Jest.
+
تسهل أدوات <code>ReactTestUtils</code> اختبار مكوّنات React في إطار عمل الاختبار الذي تريده. يستخدم Facebook الأداة [https://facebook.github.io/jest/ Jest] لاختبار JavaScript بسهولة. تعلم كيفية البدء مع Jest عبر [http://facebook.github.io/jest/docs/en/tutorial-react.html#content درس React] في موقع Jest.
  
'''ملاحظة:''' أطلقت Airbnb أداة اختبار تُدعى Enzyme والتي تجعل من السهل التعامل مع ناتج مكوّناتك. إن قرّرت استخدام أداة اختبار أخرى مع Jest فقد تستحق تجربتها [http://airbnb.io/enzyme/ من هنا].
+
'''ملاحظة:''' هنالك أداة بديلة أخرى تُدعى <code>[https://git.io/react-testing-library react-testing-library]</code> نوصي باستعمالها، إذ صُمِّمَت لتمكين وتشجيع كتابة اختبارات لاستخدامها مع مكوّناتك بينما يستخدمها المستخدم النهائي.
  
هنالك أداة بديلة أخرى تُدعى <code>[https://git.io/react-testing-library react-testing-library]</code> مُصمَّمة لتمكين وتشجيع كتابة اختبارات لاستخدامها مع مكوّناتك بينما يستخدمها المستخدم النهائي. تعمل أيضًا مع أي مُشغِّل اختبار.
+
أطلقت Airbnb أداة اختبار تُدعى [https://airbnb.io/enzyme/ Enzyme] والتي تجعل من السهل التعامل مع ناتج مكوّناتك. إن قرّرت استخدام أداة اختبار أخرى مع Jest فقد تستحق تجربتها [http://airbnb.io/enzyme/ من هنا].
  
 
== مرجع ==
 
== مرجع ==
  
=== التصيير السطحي (Shallow Rendering) ===
+
=== <code>act()‎</code> ===
عند كتابة وحدات اختبار لمكتبة React يكون التصيير السطحي مفيدًا لك. يُتيح لك التصيير السطحي تصيير مكوّن على عمق مستوى واحد وتوضيح الحقائق حول ما يُعيده تابع التصيير، بدون القلق حول سلوك المكوّنات الأبناء والتي لم تُصيَّر أو ينشأ عنها نسخة. لا يحتاج ذلك إلى DOM.
+
إن أردت تحضير مكون لإجراء عمليات اختبار عليه، فغلِّف الشيفرة التي تصيّره ونفِّذ التحديثات داخل الاستدعاء <code>act()‎</code>. يؤدي ذلك إلى تنفيذ عمليات الاختبار بشكل مشابه لكيفية عمل React في المتصفح.
  
ملاحظة: انتقل التصيير السطحي إلى <code>react-test-renderer/shallow</code>. تعلم المزيد حول [[React/shallow renderer|التصيير السطحي]] في صفحة التوثيق الخاصّة به.
+
'''ملاحظة''': إن كنت تستعمل <code>react-test-renderer</code>، فإنَّها توفر التصدير <code>act</code> الذي يسلك نفس السلوك.
  
=== أدوات أخرى ===
+
على سبيل المثال، ليكن لدينا المكون <code>Counter</code> التالي:<syntaxhighlight lang="javascript">
 +
class Counter extends React.Component {
 +
  constructor(props) {
 +
    super(props);
 +
    this.state = {count: 0};
 +
    this.handleClick = this.handleClick.bind(this);
 +
  }
 +
  componentDidMount() {
 +
    document.title = `You clicked ${this.state.count} times`;
 +
  }
 +
  componentDidUpdate() {
 +
    document.title = `You clicked ${this.state.count} times`;
 +
  }
 +
  handleClick() {
 +
    this.setState(state => ({
 +
      count: state.count + 1,
 +
    }));
 +
  }
 +
  render() {
 +
    return (
 +
      <div>
 +
        <p>You clicked {this.state.count} times</p>
 +
        <button onClick={this.handleClick}>
 +
          Click me
 +
        </button>
 +
      </div>
 +
    );
 +
  }
 +
}
  
==== Simulate ====
+
</syntaxhighlight>إليك كيف يمكن اختباره:<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
+
import React from 'react';
Simulate.{eventName}(
+
import ReactDOM from 'react-dom';
  element,
+
import { act } from 'react-dom/test-utils';
  [eventData]
+
import Counter from './Counter';
)
 
 
 
</syntaxhighlight>محاكاة حدث في عقدة DOM مع خيار إضافي لبيانات الأحداث <code>eventData</code>.
 
  
تمتلك الأداة Simulate تابعًا [[React/events#.D8.A7.D9.84.D8.A3.D8.AD.D8.AF.D8.A7.D8.AB .D8.A7.D9.84.D9.85.D8.AF.D8.B9.D9.88.D9.85.D8.A9|لكل حدث تفهمه React]].
+
let container;
  
الضغط على العنصر:<syntaxhighlight lang="javascript">
+
beforeEach(() => {
// <button ref={(node) => this.button = node}>...</button>
+
  container = document.createElement('div');
const node = this.button;
+
  document.body.appendChild(container);
ReactTestUtils.Simulate.click(node);
+
});
  
</syntaxhighlight>تغيير القيمة لحقل الإدخال ثمّ الضغط على <code>ENTER</code>:<syntaxhighlight lang="javascript">
+
afterEach(() => {
// <input ref={(node) => this.textInput = node} />
+
  document.body.removeChild(container);
const node = this.textInput;
+
  container = null;
node.value = 'giraffe';
+
});
ReactTestUtils.Simulate.change(node);
 
ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
 
  
</syntaxhighlight>'''ملاحظة:''' يجب عليك تزويد أي خاصيّة حدث تستخدمها في مكوّنك (مثل <code>KeyCode</code>، و <code>Which</code>، إلخ...) لأنّ React لا تُنشِئ أي من هذه الخاصيّات لأجلك.
+
it('can render and update a counter', () => {
 +
  // componentDidMount و render اختبر أولًا
 +
  act(() => {
 +
    ReactDOM.render(<Counter />, container);
 +
  });
 +
  const button = container.querySelector('button');
 +
  const label = container.querySelector('p');
 +
  expect(label.textContent).toBe('You clicked 0 times');
 +
  expect(document.title).toBe('You clicked 0 times');
  
==== <code>renderIntoDocument()‎</code> ====
+
  // componentDidUpdate و render اختبر ثانيًا
<syntaxhighlight lang="javascript">
+
  act(() => {
renderIntoDocument(element)
+
    button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
</syntaxhighlight>تصيير عنصر React في عقدة DOM منفصلة في المستند. تتطلّب هذه الدالة قابلية الوصول إلى DOM.
+
  });
 +
  expect(label.textContent).toBe('You clicked 1 times');
 +
  expect(document.title).toBe('You clicked 1 times');
 +
});
 +
</syntaxhighlight>لا تنسَ أنَّ إرسال أحداث DOM يعمل عند إضافة حاوية DOM إلى <code>document</code> فقط. تستطيع استعمال مساعد مثل [https://github.com/kentcdodds/react-testing-library react-testing-library] لتقليل الشيفرة المتداولة (boilerplate code).
  
'''ملاحظة:''' تحتاج إلى أن تكون <code>window</code>، و <code>window.document</code>، و <code>window.document.createElement</code> متوفرة لديك بشكل عام (global) قبل استيراد React، وإلّا ستعتقد React أنّها لا تستطيع الوصول إلى DOM ولن تعمل توابع مثل <code>setState</code>.
+
يحتوي توثيق <code>[[React/testing-recipes|recipes]]</code> على مزيد من التفاصيل حول سلوك <code>act()</code>، مع أمثلة على كيفية الاستخدام.
  
==== <code>mockComponent()‎</code> ====
+
=== <code>mockComponent()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
mockComponent(
 
mockComponent(
سطر 65: سطر 98:
 
</syntaxhighlight>تمرير وحدة المكوّن المحاكي إلى هذا التابع لتزويد المكوّن بتوابع مفيدة، فبدلًا من التصيير الاعتيادي سيصبح المكوّن عنصر <code>div</code> بسيط (أو أي عنصر آخر ندخله ضمن الوسيط <code>mockTagName</code>) يحتوي على أي مكوّن ابن نعطيه له.
 
</syntaxhighlight>تمرير وحدة المكوّن المحاكي إلى هذا التابع لتزويد المكوّن بتوابع مفيدة، فبدلًا من التصيير الاعتيادي سيصبح المكوّن عنصر <code>div</code> بسيط (أو أي عنصر آخر ندخله ضمن الوسيط <code>mockTagName</code>) يحتوي على أي مكوّن ابن نعطيه له.
  
'''ملاحظة:''' <code>mockComponent()</code>‎ هي واجهة برمجة تطبيق قديمة. نوصي باستخدام [[React/test utils#.D8.A7.D9.84.D8.AA.D8.B5.D9.8A.D9.8A.D8.B1 .D8.A7.D9.84.D8.B3.D8.B7.D8.AD.D9.8A .28Shallow Rendering.29|التصيير السطحي]] أو <code>[https://facebook.github.io/jest/docs/en/tutorial-react-native.html#mock-native-modules-using-jestmock jest.mock()]</code>‎ بدلًا من ذلك.
+
'''ملاحظة:''' <code>mockComponent()</code>‎ هي واجهة برمجة تطبيق قديمة. نوصي باستخدام <code>[https://facebook.github.io/jest/docs/en/tutorial-react-native.html#mock-native-modules-using-jestmock jest.mock()]</code>‎ بدلًا من ذلك.
  
==== <code>isElement()‎</code> ====
+
=== <code>isElement()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
isElement(element)
 
isElement(element)
 
</syntaxhighlight>يُعيد القيمة <code>true</code> إن كان الوسيط <code>element</code> عبارة عن عنصر React.
 
</syntaxhighlight>يُعيد القيمة <code>true</code> إن كان الوسيط <code>element</code> عبارة عن عنصر React.
  
==== <code>isElementOfType()‎</code> ====
+
=== <code>isElementOfType()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
isElementOfType(
 
isElementOfType(
سطر 81: سطر 114:
 
</syntaxhighlight>يُعيد القيمة <code>true</code> إن كان الوسيط <code>element</code> عبارة عن عنصر React نوعه هو <code>componentClass</code>.
 
</syntaxhighlight>يُعيد القيمة <code>true</code> إن كان الوسيط <code>element</code> عبارة عن عنصر React نوعه هو <code>componentClass</code>.
  
==== <code>isDOMComponent()‎</code> ====
+
=== <code>isDOMComponent()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
isDOMComponent(instance)
 
isDOMComponent(instance)
 
</syntaxhighlight>يُعيد القيمة <code>true</code> إن كان الوسيط <code>instance</code> هو مكوّن DOM (مثل <code>div</code> أو <code>span</code>).
 
</syntaxhighlight>يُعيد القيمة <code>true</code> إن كان الوسيط <code>instance</code> هو مكوّن DOM (مثل <code>div</code> أو <code>span</code>).
  
==== <code>isCompositeComponent()‎</code> ====
+
=== <code>isCompositeComponent()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
isCompositeComponent(instance)
 
isCompositeComponent(instance)
 
</syntaxhighlight>يُعيد القيمة <code>true</code> إن كان الوسيط <code>instance</code> عبارة عن مكوّن مُعرَّف من قبل المستخدم كالأصناف أو الدوال.
 
</syntaxhighlight>يُعيد القيمة <code>true</code> إن كان الوسيط <code>instance</code> عبارة عن مكوّن مُعرَّف من قبل المستخدم كالأصناف أو الدوال.
  
==== <code>isCompositeComponentWithType()‎</code> ====
+
=== <code>isCompositeComponentWithType()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
isCompositeComponentWithType(
 
isCompositeComponentWithType(
سطر 100: سطر 133:
 
</syntaxhighlight>يُعيد القيمة <code>true</code> إن كان الوسيط <code>instance</code> عبارة عن مكوّن من النوع <code>componentClass</code>.
 
</syntaxhighlight>يُعيد القيمة <code>true</code> إن كان الوسيط <code>instance</code> عبارة عن مكوّن من النوع <code>componentClass</code>.
  
==== <code>findAllInRenderedTree()‎</code> ====
+
=== <code>findAllInRenderedTree()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
findAllInRenderedTree(
 
findAllInRenderedTree(
سطر 109: سطر 142:
 
</syntaxhighlight>التنقل عبر مكوّنات شجرة المكوّنات وجمع كل المكوّنات حيث يكون <code>test(component)</code>‎ قيمته <code>true</code>. لا يكون هذا مفيدًا بحد ذاته ولكنّه يُستخدَم كبداية لأدوات اختبار أخرى.
 
</syntaxhighlight>التنقل عبر مكوّنات شجرة المكوّنات وجمع كل المكوّنات حيث يكون <code>test(component)</code>‎ قيمته <code>true</code>. لا يكون هذا مفيدًا بحد ذاته ولكنّه يُستخدَم كبداية لأدوات اختبار أخرى.
  
==== <code>scryRenderedDOMComponentsWithClass()‎</code> ====
+
=== <code>scryRenderedDOMComponentsWithClass()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
scryRenderedDOMComponentsWithClass(
 
scryRenderedDOMComponentsWithClass(
سطر 118: سطر 151:
 
</syntaxhighlight>إيجاد جميع مكوّنات عناصر DOM في الشجرة المُصيَّرة والتي هي مكوّنات DOM لها اسم صنف يُطابِق المذكور في الوسيط <code>className</code>.
 
</syntaxhighlight>إيجاد جميع مكوّنات عناصر DOM في الشجرة المُصيَّرة والتي هي مكوّنات DOM لها اسم صنف يُطابِق المذكور في الوسيط <code>className</code>.
  
==== <code>findRenderedDOMComponentWithClass()‎</code> ====
+
=== <code>findRenderedDOMComponentWithClass()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
findRenderedDOMComponentWithClass(
 
findRenderedDOMComponentWithClass(
سطر 127: سطر 160:
 
</syntaxhighlight>مثل <code>scryRenderedDOMComponentsWithClass()</code>‎ ولكن يتوقّع وجود نتيجة واحدة، ويُعيد تلك النتيجة، أو يرمي استثناء إن كان عدد المكوّنات المطابقة أكثر من واحد.
 
</syntaxhighlight>مثل <code>scryRenderedDOMComponentsWithClass()</code>‎ ولكن يتوقّع وجود نتيجة واحدة، ويُعيد تلك النتيجة، أو يرمي استثناء إن كان عدد المكوّنات المطابقة أكثر من واحد.
  
==== <code>scryRenderedDOMComponentsWithTag()‎</code> ====
+
=== <code>scryRenderedDOMComponentsWithTag()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
scryRenderedDOMComponentsWithTag(
 
scryRenderedDOMComponentsWithTag(
سطر 136: سطر 169:
 
</syntaxhighlight>إيجاد جميع مكوّنات عناصر DOM في الشجرة المُصيَّرة والتي هي مكوّنات DOM لها اسم للوسم يُطابِق المذكور في الوسيط <code>tagName</code>.
 
</syntaxhighlight>إيجاد جميع مكوّنات عناصر DOM في الشجرة المُصيَّرة والتي هي مكوّنات DOM لها اسم للوسم يُطابِق المذكور في الوسيط <code>tagName</code>.
  
==== <code>findRenderedDOMComponentWithTag()‎</code> ====
+
=== <code>findRenderedDOMComponentWithTag()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
findRenderedDOMComponentWithTag(
 
findRenderedDOMComponentWithTag(
سطر 145: سطر 178:
 
</syntaxhighlight>مثل <code>scryRenderedDOMComponentsWithTag()</code>‎ ولكن يتوقّع وجود نتيجة واحدة، ويُعيد تلك النتيجة، أو يرمي استثناء إن كان عدد المكوّنات المطابقة أكثر من واحد.
 
</syntaxhighlight>مثل <code>scryRenderedDOMComponentsWithTag()</code>‎ ولكن يتوقّع وجود نتيجة واحدة، ويُعيد تلك النتيجة، أو يرمي استثناء إن كان عدد المكوّنات المطابقة أكثر من واحد.
  
==== <code>scryRenderedComponentsWithType()‎</code> ====
+
=== <code>scryRenderedComponentsWithType()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
scryRenderedComponentsWithType(
 
scryRenderedComponentsWithType(
سطر 154: سطر 187:
 
</syntaxhighlight>إيجاد جميع نسخ المكوّنات التي نوعها يُساوي <code>componentClass</code>.
 
</syntaxhighlight>إيجاد جميع نسخ المكوّنات التي نوعها يُساوي <code>componentClass</code>.
  
==== <code>findRenderedComponentWithType()‎</code> ====
+
=== <code>findRenderedComponentWithType()‎</code> ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
findRenderedComponentWithType(
 
findRenderedComponentWithType(
سطر 162: سطر 195:
  
 
</syntaxhighlight>مثل <code>scryRenderedComponentsWithType()‎‎</code> ولكن يتوقّع وجود نتيجة واحدة، ويُعيد تلك النتيجة، أو يرمي استثناء إن كان عدد المكوّنات المطابقة أكثر من واحد.
 
</syntaxhighlight>مثل <code>scryRenderedComponentsWithType()‎‎</code> ولكن يتوقّع وجود نتيجة واحدة، ويُعيد تلك النتيجة، أو يرمي استثناء إن كان عدد المكوّنات المطابقة أكثر من واحد.
 +
=== <code>renderIntoDocument()‎</code> ===
 +
<syntaxhighlight lang="javascript">
 +
renderIntoDocument(element)
 +
</syntaxhighlight>تصيير عنصر React في عقدة DOM منفصلة في المستند. تتطلّب هذه الدالة قابلية الوصول إلى DOM. وهي مكافئة لما يلي:<syntaxhighlight lang="javascript">
 +
const domContainer = document.createElement('div');
 +
ReactDOM.render(element, domContainer);
 +
 +
</syntaxhighlight>'''ملاحظة:''' تحتاج إلى أن تكون <code>window</code>، و <code>window.document</code>، و <code>window.document.createElement</code> متوفرة لديك بشكل عام (global) قبل استيراد React، وإلّا ستعتقد React أنّها لا تستطيع الوصول إلى DOM ولن تعمل توابع مثل <code>setState</code>.
 +
 +
=== أدوات أخرى ===
 +
 +
==== <code>Simulate</code> ====
 +
<syntaxhighlight lang="javascript">
 +
Simulate.{eventName}(
 +
  element,
 +
  [eventData]
 +
)
 +
 +
</syntaxhighlight>محاكاة حدث في عقدة DOM مع خيار إضافي لبيانات الأحداث <code>eventData</code>.
 +
 +
تمتلك الأداة Simulate تابعًا [[React/events#.D8.A7.D9.84.D8.A3.D8.AD.D8.AF.D8.A7.D8.AB .D8.A7.D9.84.D9.85.D8.AF.D8.B9.D9.88.D9.85.D8.A9|لكل حدث تفهمه React]].
 +
 +
الضغط على عنصر:<syntaxhighlight lang="javascript">
 +
// <button ref={(node) => this.button = node}>...</button>
 +
const node = this.button;
 +
ReactTestUtils.Simulate.click(node);
 +
 +
</syntaxhighlight>تغيير القيمة لحقل الإدخال ثمّ الضغط على <code>ENTER</code>:<syntaxhighlight lang="javascript">
 +
// <input ref={(node) => this.textInput = node} />
 +
const node = this.textInput;
 +
node.value = 'giraffe';
 +
ReactTestUtils.Simulate.change(node);
 +
ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
 +
 +
</syntaxhighlight>'''ملاحظة:''' يجب عليك تزويد أي خاصيّة حدث تستخدمها في مكوّنك (مثل <code>KeyCode</code>، و <code>Which</code>، إلخ...) لأنّ React لا تُنشِئ أي من هذه الخاصيّات لأجلك.
 +
== انظر أيضًا ==
 +
* [[React/react api|واجهة برمجة التطبيق (API) ذات المستوى الأعلى في React]]
 +
* [[React/react component|الصنف React.Component]]
 +
* [[React/react dom|الكائن ReactDOM]]
 +
* [[React/react dom server|الكائن ReactDOMServer]]
 +
* [[React/dom elements|عناصر DOM]]
 +
* [[React/events|الأحداث المصطنعة (Synthetic Events)]]
 +
* [[React/shallow renderer|التصيير السطحي (Shallow Rendering)]]
 +
* [[React/test renderer|مصير الاختبار (Test Renderer)]]
 +
* [[React/javascript environment requirements|متطلبات بيئة JavaScript]]
 
==مصادر==
 
==مصادر==
 
*[https://reactjs.org/docs/test-utils.html صفحة أدوات الاختبار في توثيق React الرسمي].
 
*[https://reactjs.org/docs/test-utils.html صفحة أدوات الاختبار في توثيق React الرسمي].
 
[[تصنيف:React]]
 
[[تصنيف:React]]
 +
[[تصنيف:React API Reference]]

المراجعة الحالية بتاريخ 21:05، 5 نوفمبر 2020

استيراد الأدوات

import ReactTestUtils from 'react-dom/test-utils'; // ES6
var ReactTestUtils = require('react-dom/test-utils'); // ES5 with npm

لمحة عامة

تسهل أدوات ReactTestUtils اختبار مكوّنات React في إطار عمل الاختبار الذي تريده. يستخدم Facebook الأداة Jest لاختبار JavaScript بسهولة. تعلم كيفية البدء مع Jest عبر درس React في موقع Jest.

ملاحظة: هنالك أداة بديلة أخرى تُدعى react-testing-library نوصي باستعمالها، إذ صُمِّمَت لتمكين وتشجيع كتابة اختبارات لاستخدامها مع مكوّناتك بينما يستخدمها المستخدم النهائي.

أطلقت Airbnb أداة اختبار تُدعى Enzyme والتي تجعل من السهل التعامل مع ناتج مكوّناتك. إن قرّرت استخدام أداة اختبار أخرى مع Jest فقد تستحق تجربتها من هنا.

مرجع

act()‎

إن أردت تحضير مكون لإجراء عمليات اختبار عليه، فغلِّف الشيفرة التي تصيّره ونفِّذ التحديثات داخل الاستدعاء act()‎. يؤدي ذلك إلى تنفيذ عمليات الاختبار بشكل مشابه لكيفية عمل React في المتصفح.

ملاحظة: إن كنت تستعمل react-test-renderer، فإنَّها توفر التصدير act الذي يسلك نفس السلوك.

على سبيل المثال، ليكن لدينا المكون Counter التالي:

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 0};
    this.handleClick = this.handleClick.bind(this);
  }
  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }
  handleClick() {
    this.setState(state => ({
      count: state.count + 1,
    }));
  }
  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={this.handleClick}>
          Click me
        </button>
      </div>
    );
  }
}

إليك كيف يمكن اختباره:

import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import Counter from './Counter';

let container;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  document.body.removeChild(container);
  container = null;
});

it('can render and update a counter', () => {
  // componentDidMount و render اختبر أولًا
  act(() => {
    ReactDOM.render(<Counter />, container);
  });
  const button = container.querySelector('button');
  const label = container.querySelector('p');
  expect(label.textContent).toBe('You clicked 0 times');
  expect(document.title).toBe('You clicked 0 times');

  // componentDidUpdate و render اختبر ثانيًا
  act(() => {
    button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
  });
  expect(label.textContent).toBe('You clicked 1 times');
  expect(document.title).toBe('You clicked 1 times');
});

لا تنسَ أنَّ إرسال أحداث DOM يعمل عند إضافة حاوية DOM إلى document فقط. تستطيع استعمال مساعد مثل react-testing-library لتقليل الشيفرة المتداولة (boilerplate code).

يحتوي توثيق recipes على مزيد من التفاصيل حول سلوك act()‎، مع أمثلة على كيفية الاستخدام.

mockComponent()‎

mockComponent(
  componentClass,
  [mockTagName]
)

تمرير وحدة المكوّن المحاكي إلى هذا التابع لتزويد المكوّن بتوابع مفيدة، فبدلًا من التصيير الاعتيادي سيصبح المكوّن عنصر div بسيط (أو أي عنصر آخر ندخله ضمن الوسيط mockTagName) يحتوي على أي مكوّن ابن نعطيه له.

ملاحظة: mockComponent()‎ هي واجهة برمجة تطبيق قديمة. نوصي باستخدام jest.mock()‎ بدلًا من ذلك.

isElement()‎

isElement(element)

يُعيد القيمة true إن كان الوسيط element عبارة عن عنصر React.

isElementOfType()‎

isElementOfType(
  element,
  componentClass
)

يُعيد القيمة true إن كان الوسيط element عبارة عن عنصر React نوعه هو componentClass.

isDOMComponent()‎

isDOMComponent(instance)

يُعيد القيمة true إن كان الوسيط instance هو مكوّن DOM (مثل div أو span).

isCompositeComponent()‎

isCompositeComponent(instance)

يُعيد القيمة true إن كان الوسيط instance عبارة عن مكوّن مُعرَّف من قبل المستخدم كالأصناف أو الدوال.

isCompositeComponentWithType()‎

isCompositeComponentWithType(
  instance,
  componentClass
)

يُعيد القيمة true إن كان الوسيط instance عبارة عن مكوّن من النوع componentClass.

findAllInRenderedTree()‎

findAllInRenderedTree(
  tree,
  test
)

التنقل عبر مكوّنات شجرة المكوّنات وجمع كل المكوّنات حيث يكون test(component)‎ قيمته true. لا يكون هذا مفيدًا بحد ذاته ولكنّه يُستخدَم كبداية لأدوات اختبار أخرى.

scryRenderedDOMComponentsWithClass()‎

scryRenderedDOMComponentsWithClass(
  tree,
  className
)

إيجاد جميع مكوّنات عناصر DOM في الشجرة المُصيَّرة والتي هي مكوّنات DOM لها اسم صنف يُطابِق المذكور في الوسيط className.

findRenderedDOMComponentWithClass()‎

findRenderedDOMComponentWithClass(
  tree,
  className
)

مثل scryRenderedDOMComponentsWithClass()‎ ولكن يتوقّع وجود نتيجة واحدة، ويُعيد تلك النتيجة، أو يرمي استثناء إن كان عدد المكوّنات المطابقة أكثر من واحد.

scryRenderedDOMComponentsWithTag()‎

scryRenderedDOMComponentsWithTag(	
  tree,
  tagName
)

إيجاد جميع مكوّنات عناصر DOM في الشجرة المُصيَّرة والتي هي مكوّنات DOM لها اسم للوسم يُطابِق المذكور في الوسيط tagName.

findRenderedDOMComponentWithTag()‎

findRenderedDOMComponentWithTag(
  tree,
  tagName
)

مثل scryRenderedDOMComponentsWithTag()‎ ولكن يتوقّع وجود نتيجة واحدة، ويُعيد تلك النتيجة، أو يرمي استثناء إن كان عدد المكوّنات المطابقة أكثر من واحد.

scryRenderedComponentsWithType()‎

scryRenderedComponentsWithType(
  tree,
  componentClass
)

إيجاد جميع نسخ المكوّنات التي نوعها يُساوي componentClass.

findRenderedComponentWithType()‎

findRenderedComponentWithType(
  tree,
  componentClass
)

مثل scryRenderedComponentsWithType()‎‎ ولكن يتوقّع وجود نتيجة واحدة، ويُعيد تلك النتيجة، أو يرمي استثناء إن كان عدد المكوّنات المطابقة أكثر من واحد.

renderIntoDocument()‎

renderIntoDocument(element)

تصيير عنصر React في عقدة DOM منفصلة في المستند. تتطلّب هذه الدالة قابلية الوصول إلى DOM. وهي مكافئة لما يلي:

const domContainer = document.createElement('div');
ReactDOM.render(element, domContainer);

ملاحظة: تحتاج إلى أن تكون window، و window.document، و window.document.createElement متوفرة لديك بشكل عام (global) قبل استيراد React، وإلّا ستعتقد React أنّها لا تستطيع الوصول إلى DOM ولن تعمل توابع مثل setState.

أدوات أخرى

Simulate

Simulate.{eventName}(
  element,
  [eventData]
)

محاكاة حدث في عقدة DOM مع خيار إضافي لبيانات الأحداث eventData.

تمتلك الأداة Simulate تابعًا لكل حدث تفهمه React.

الضغط على عنصر:

// <button ref={(node) => this.button = node}>...</button>
const node = this.button;
ReactTestUtils.Simulate.click(node);

تغيير القيمة لحقل الإدخال ثمّ الضغط على ENTER:

// <input ref={(node) => this.textInput = node} />
const node = this.textInput;
node.value = 'giraffe';
ReactTestUtils.Simulate.change(node);
ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});

ملاحظة: يجب عليك تزويد أي خاصيّة حدث تستخدمها في مكوّنك (مثل KeyCode، و Which، إلخ...) لأنّ React لا تُنشِئ أي من هذه الخاصيّات لأجلك.

انظر أيضًا

مصادر