Thử nghiệm phát triển theo hướng phản ứng với Jest và Enzyme

https://ichi.pro/vi/thu-nghiem-phat-trien-theo-huong-phan-ung-voi-jest-va-enzyme-46647418302577

Giới thiệu: Làm thế nào để bạn phát triển?

Bài nói chuyện này bao gồm một loạt các công cụ kiểm tra mà Enzyme và Jest cung cấp cho React, từ kiểm tra ảnh chụp nhanh , các hàm giả , các sự kiện mô phỏng và cách kiểm tra các chức năng, đạo cụ và trạng thái của thành phần. Chúng tôi cũng sẽ khám phá các khía cạnh khác của kiểm thử bao gồm khả năng trừu tượng và sự cô lập logic trong các dự án React của bạn, và cuối cùng kết thúc bằng một danh sách kiểm tra để tuân thủ khi thiết kế các bài kiểm tra của riêng bạn.

Enzymeđùa là những công cụ quan trọng mà có thể đảm bảo một cao mức độ ổn định cho mã của bạn từ việc triển khai đầu tiên của bạn , và có thể được sử dụng cho dù bạn đang áp dụng một Test Driven Development hoặc hành vi Driven Development chiến lược. Trước khi đi sâu vào các công cụ, hãy xem xét sự khác biệt giữa hai cách tiếp cận phát triển này một cách trừu tượng.

Phát triển theo hướng hành vi

Bạn dành bao nhiêu thời gian để thử nghiệm ứng dụng của mình? Nó được thực hiện sau một cột mốc quan trọng hoặc trong khoảng thời gian khi bạn đang phát triển các thành phần ? Cách tiếp cận trước đây ưu tiên hành vi của ứng dụng, ưu tiên hoàn thành logic nghiệp vụ hơn tính ổn định của thành phần. Các bộ thử nghiệm của bạn sẽ được mã hóa khi quá trình triển khai hành vi hoàn tất, kết quả của các thử nghiệm đó xác định lộ trình tốt nhất để cấu trúc lại mã của bạn hoặc sửa bất kỳ lỗ hổng nào trên đường đi.

Về bản chất, cách tiếp cận này đang làm là kiểm tra tích hợp các thành phần của bạn trước khi viết các bài kiểm tra đơn vị của bạn - hoặc bất kỳ bài kiểm tra chính thức nào bằng cách sử dụng một khuôn khổ phức tạp như Jest. Điều này thực sự mang lại sự quay vòng nhanh hơn và được áp dụng khá rộng rãi. Tại sao điều này là trường hợp? Có thể là do các cuộc họp kinh doanh hiếm khi thảo luận về các ứng dụng từ góc độ định hướng thử nghiệm - trọng tâm luôn là kết quả cuối cùng hoặc hành vi dự kiến ​​được đặt ra để đạt được. Việc nghiêng về BDD được cho là điều tự nhiên với những yếu tố này; ghi chú cuộc họp sẽ hoàn toàn dựa trên hành vi, với một lịch trình chặt chẽ để hoàn thành công việc.

Bây giờ, điều này có thể tuyệt vời để tạo mẫu nhanh hoặc tạo bằng chứng về khái niệm - nhưng đối với các ứng dụng có cơ sở mã lớn, tích hợp phức tạp với một loạt dịch vụ hoặc đơn giản là một nhóm lớn đồng thời đóng góp ( một blockchain là một ví dụ tuyệt vời cho tất cả những điều này các trường hợp ), phương pháp tiếp cận đầu tiên thử nghiệm để phát triển có ý nghĩa rất nhiều, dưới dạng Phát triển theo hướng thử nghiệm - và phương pháp này cũng tự nhiên phù hợp với phát triển React.

Hướng phát triển thử nghiệm

Cho dù bạn đang phát triển trải nghiệm người dùng giao diện người dùng hay gói NPM mà hàng nghìn nhà phát triển và hàng triệu người dùng cuối tin cậy, thì tính ổn định và độ tin cậy sẽ là một trong những ưu tiên cao nhất cho cơ sở mã của bạn. Những gì TDD làm là quảng bá mã sạch và có chức năng, khiến nhà phát triển cân nhắc những thứ như cách thành phần sẽ được tích hợp, cách tái sử dụng và khả năng tái cấu trúc của nó.

TDD thúc đẩy phát triển với mục đích hơn là thủ tục . Bằng cách này, rất nhiều sự phức tạp của việc kết hợp toàn bộ trải nghiệm với nhau sẽ được loại bỏ. Các thành phần React đã làm rất tốt trong việc phân chia logic, làm cho chúng phù hợp với TDD, theo đó các bài kiểm tra đơn vị và kiểm tra tích hợp có thể được thực hiện trên cơ sở mỗi thành phần.

Cuối cùng, TDD nhằm mục đích giải quyết vấn đề cung cấp phần mềm có lỗi cho người tiêu dùng cuối cùng bằng cách coi quy trình thử nghiệm như một phần của quá trình phát triển , thay vì một công việc phải làm ở cuối nước rút. Tất nhiên, việc giảm thiểu lỗi là vì lợi ích của toàn bộ tổ chức chứ không chỉ riêng nhóm phát triển. Nói cách khác, kiểm thử phải là điều mà tất cả mọi người đều quan tâm, không chỉ các nhà phát triển.

Tất cả đều rất trừu tượng, vì vậy chúng ta hãy đi sâu vào TDD cụ thể của React để hiểu những gì có thể được thực hiện ở đây.

Bài viết này dựa trên một số kiến ​​thức cơ bản về Enzyme và Jest. Nếu bạn muốn làm quen với Jest và Enzyme ở giai đoạn này, vui lòng tham khảo bài viết giới thiệu của tôi về các gói:Thử nghiệm trong React với Jest và Enzyme: Giới thiệu

Cấu trúc các thành phần phản ứng để kiểm tra

Các nhà phát triển React quen với việc phát triển các thành phần sẽ biết rằng logic thành phần có thể xây dựng rất nhanh khi các thay đổi trạng thái, lệnh gọi API và các logic khác như ánh xạ được giới thiệu. Điều chúng tôi muốn làm đối với logic này là đảm bảo rằng nó hoạt động trong mọi trường hợp mà nó sẽ được sử dụng . Để làm được điều này, chúng tôi muốn đạt được 2 điều:

1. Kiểm tra logic một cách cô lập

Mã trừu tượng trở thành mối quan tâm chính trong TDD, điều này cũng gián tiếp thúc đẩy các nguyên tắc như KHÔ (Không lặp lại bản thân) để có khả năng tái sử dụng thành phần tối đa. Điều này dễ dàng đạt được trong React với sự linh hoạt của các thành phần.

Hãy xem xét một dự án ví dụ, một ứng dụng điều hướng có <SideBar /><TopBar />thành phần - cả hai đều sử dụng một <Item />thành phần cho các liên kết điều hướng. Bằng cách tách từng thành phần (và kiểu dáng chuyên dụng của chúng), chúng tôi đang cô lập logic cũng như các bài kiểm tra liên quan đến từng thành phần.

Hãy nhớ rằng, TDD với sự cô lập thành phần sẽ giới hạn mục đích của thành phần chỉ để làm một việc và bác bỏ quan điểm về việc nó làm bất cứ điều gì khác ngoài việc đó.

Kiểm tra cấu trúc dự án ứng dụng điều hướng giả định này:

// @rossbulat/nav package
src/
   Item/
      index.js
      index.scss
   SideBar/
      index.js
      index.scss
   TopBar/
      index.js
      index.scss
tests/
    Item.test.js
    nav.test.js
    SideBar.test.js
    TopBar.test.js
// @rossbulat/app
src/
   App.js
   api.js
   Home/
      index.js
      index.scss
tests/
  app.test.js
  api.test.js
// src/Home/index.js
import { SideBar, TopBar } from '@rossbulat/nav';

Tách cả hai <SideBar /><TopBar />đảm bảo rằng tôi có thể kiểm tra từng thành phần một cách độc lập. Không chỉ vậy, mỗi liên kết điều hướng bao gồm một <Item />thành phần; thành phần này có thể bao gồm đạo cụ như title, link, icon, và nhiều hơn nữa - tất cả đều có thể được kiểm tra một cách cô lập.

Cũng như các bài kiểm tra thành phần riêng biệt của tôi, tôi cũng có một nav.test.jstệp có thể bao gồm các bài kiểm tra tích hợp của tôi hoặc cả 3 thành phần hoạt động cùng nhau, để đảm bảo chúng tuân thủ ảnh chụp nhanh dự kiến ​​- có thể kiểm tra xem có bao nhiêu <li>thẻ được lồng trong <SideBar />đánh dấu để trùng khớp với cách nhiều <Item />thành phần có mặt. Chúng tôi sẽ ghé thăm ảnh chụp nhanh hơn nữa.

API của tôi cũng hoàn toàn tách biệt với các thành phần khác trong dự án ứng dụng chính của tôi; các api.test.jstập tin là đặc biệt cho các cuộc gọi API, mà không có nghi ngờ sẽ bao gồm các chức năng mô hình khác nhau để phục vụ cho các cuộc gọi API giả. Một lần nữa, chúng ta sẽ khám phá những lời chế giễu sâu hơn trong bài nói chuyện này.

2. Kiểm tra mã trong một loạt các tình huống

Điều này chỉ có thể đạt được nếu đáp ứng điều kiện trước của logic trừu tượng hóa, theo đó một loạt các đạo cụ có thể mở rộng có thể được chuyển vào thành phần hoặc trong trường hợp kiểm tra tích hợp, một số lượng mở rộng các thành phần khác hoạt động cùng nhau.

Với sức mạnh của sự cô lập, tiếp theo hãy khám phá một số ví dụ mã hữu hình về việc thử nghiệm các thành phần React.

Có lẽ cách đơn giản nhất để kiểm tra một thành phần React là so sánh kết quả của một hàm kết xuất với một tệp ảnh chụp nhanh . Jest cung cấp cho chúng tôi các công cụ cần thiết để thực hiện chính xác điều này.

Sử dụng Ảnh chụp nhanh với Jest

Sử dụng thuật ngữ của Jest, kiểm tra ảnh chụp nhanh là một tính năng hữu ích để đảm bảo rằng đánh dấu của bạn không thay đổi bất ngờ và cũng đúng như vậy, đảm bảo rằng điều đó sẽ render()xuất ra những gì bạn dự định.

Kiểm tra ảnh chụp nhanh đối với các thành phần có thể được thực hiện bằng phương pháp Enzyme (mà chúng ta sẽ thấy bên dưới) nhưng cũng có thể được sử dụng với gói phản ứng-thử nghiệm-kết xuất (cực kỳ phổ biến với hơn 2 triệu lượt tải xuống hàng tuần tại thời điểm xuất bản). Thêm nó vào dự án của bạn thông qua NPM hoặc sợi:

yarn add react-test-renderer
expect(<component>).toMatchSnapshot();

Mặc dù chúng tôi sẽ không chỉnh sửa thủ công tệp ảnh chụp nhanh trong các trường hợp bình thường, chúng rất dễ đọc theo thiết kế cho mục đích xem xét mã và được thiết kế để thêm vào kiểm soát nguồn - cam kết tệp ảnh chụp nhanh của bạn với các bản cập nhật mã của bạn.

Hãy xem một ví dụ thực tế về kiểm tra ảnh chụp nhanh - Tôi sẽ kiểm tra xem <Item />thành phần của tôi từ trước đó có hiển thị chính xác hay không. Lưu ý rằng việc chạy thử nghiệm lần đầu tiên sẽ tạo ra tệp ảnh chụp nhanh:

import React from 'react'
import { Item } from '@rossbulat/nav'
import renderer from 'react-test-renderer'
import icon from './img/ross.png'
describe('testing navigation', () => {
 it('renders correctly', () => {
   const item = renderer
    .create(
       <Item 
         link="https://rossbulat.co" 
         text="My Homepage" 
         icon={icon} />
     ).toJSON();
expect(item).toMatchSnapshot();
 });
});
...
it('sidebar should render correctly', () => {
  const sidebar = shallow(<SideBar />);
  expect(sidebar).toMatchSnapshot();
});
it('sidebar should render links correctly', () => {
  const links = 
  [{link: 'https://rossbulat.co', text: 'My Homepage'},
   {link: 'https://medium.com/@rossbulat', text: 'Medium'}];
   const component = shallow(<MyComponent links={links} />);
   expect(component).toMatchSnapshot();
});

Nếu bạn đang chạy thử nghiệm của mình ở chế độ đồng hồ yarn test --watch, sau đó nhấn vào ichế độ chụp nhanh tương tác, các ảnh chụp nhanh không thành công sẽ bật lên khi chúng xảy ra và lỗi chính xác sẽ được ghi lại, cho phép bạn khắc phục sự cố không nhất quán.

Các tệp ảnh chụp nhanh được tạo trong một __snapshots__thư mục trong thư mục thử nghiệm của bạn, tất cả các tệp trong số đó sẽ được đặt tên <test_name>.snap.

Lưu ý: Gói được sử dụng để chuyển đổi các đối tượng Javascript thành biểu diễn chuỗi trong các .snaptệp này có định dạng khá . Gói này chỉ là một gói NPM khác và có thể được sử dụng trong các dự án của riêng bạn, nơi bạn muốn gỡ lỗi hoặc lập tài liệu triển khai Javascript.

Nếu sau khi bạn đã cập nhật giao diện người dùng và muốn cập nhật ảnh chụp nhanh của mình, chúng tôi có một lệnh hữu ích để thực hiện việc đó. Chạy phần sau trong thư mục dự án của bạn để cập nhật tất cả các ảnh chụp nhanh của bạn:

jest --updateSnapshot

Để có tài liệu đầy đủ và bộ tính năng của ảnh chụp nhanh trong Jest, hãy truy cập trang này .

Mô phỏng sự kiện

Việc mô phỏng các sự kiện như nhấp chuột và nhập văn bản có thể được thực hiện trong các thử nghiệm của chúng tôi bằng simulate()phương pháp của Enzyme . Ví dụ: giả sử tôi muốn kiểm tra kết quả của việc nhấp vào một nút bên trong một thành phần. Điều này có thể được thực hiện như vậy:

it('should update form submitted state with button click', () => {
  const component = mount(<RegistrationForm />);
  component
    .find('button#submit_form')
    .simulate('click');
  
  expect(component.state('form_submitted')).toEqual(true);
  component.unmount();
});

Trong thực tế, chúng tôi có thể cần một biểu mẫu được điền trước khi gửi biểu mẫu đăng ký, ít nhất là với một địa chỉ email hoặc số điện thoại. Chúng ta có thể dễ dàng kiểm tra các giá trị đầu vào không? Có, chúng tôi có thể, như vậy:

component
  .find('#name')
  .simulate('change', { target: { value: 'Ross' } });
component
  .find('#agreetoterms')
  .simulate('change', {target: {checked: true}});
component.find('#input').simulate('keydown', { keyCode: 70 });
const component = shallow(<MyComponent />);
const result = component.instance().callMethod();

Như tên cho thấy, các hàm Mock cho phép chúng ta triển khai lại một hàm, loại bỏ logic nhằm mục đích nắm bắt các lệnh gọi đến hàm, kiểm tra các tham số và kiểm tra các giá trị trả về.

Cách đơn giản nhất để sử dụng các hàm giả là chỉ cần xác định một hàm trống và đặt nó vào vị trí của một hàm thực trong các bài kiểm tra của bạn. Lợi ích của việc này là theo dõi xem chức năng có thực sự được gọi hay không. (Đã bao nhiêu lần bạn gỡ lỗi và nhận thấy rằng một hàm cụ thể không thực sự được gọi, dẫn đến lỗi? ).

Chúng ta có thể xác định một hàm như vậy bằng cách sử dụng jest.fn(). Đây là cách chúng tôi thay thế một onClicktrình xử lý bằng một hàm giả rỗng và kiểm tra xem nó đã được gọi hay chưa:

//define empty mock function 
const fnClick = jest.fn();
describe('click events', () => {
  it('button click should show menu', () => {
    //replace actual function with mock function
    const component = shallow(<MyButton onClick={fnClick} />);
    
   //simulate a click
   component
      .find('button#btn_open_menu')
      .simulate('click');
    
    //check if function was called
    expect(fnClick).toHaveBeenCalled();
  });
});
// Test how many times the function is called
expect(fnClick.mock.calls.length).toBe(3);
// Test the values passed as arguments
// The second argument of the third call to the function was 'yes'
expect(mockCallback.mock.calls[2][1]).toBe('yes');
// Test return values
// The return value of the second call to the function was true
expect(mockCallback.mock.results[1].value).toBe(true);

Tiêm các chức năng giả

Một điều thú vị khác mà chúng ta có thể làm với mocks là đưa chúng vào một bài kiểm tra bất cứ khi nào chúng ta muốn truy xuất giá trị trả về, với console.log():

const myMock = jest.fn();
console.log(myMock('return this string'));
// > return this string
// return `true` for first call, 
// return `false` for the second call 
// return a string 'hello mock' for the third call
myMock.mockReturnValueOnce(true)
      .mockReturnValueOnce(false)
      .mockReturnValueOnce('hello mock');
//call the mock three times to witness the results:
console.log(myMock(), myMock(), myMock());

Hãy tưởng tượng một tình huống trong đó một chuỗi JSON {result: true}được trả về thay vì true. Bản thân chuỗi JSON là giá trị true, với giá trị {result: false}cũng được đánh giá là true. Kiểm tra các giá trị trả về bằng mocks sẽ đảm bảo những trường hợp này không xảy ra.

Các chức năng giả lập có thể được sử dụng cùng với mọi thứ chúng tôi đã khám phá cho đến nay, bao gồm cả ảnh chụp nhanh và các sự kiện mô phỏng. Bạn sẽ thấy các bài kiểm tra của mình trở nên phức tạp hơn khi có nhiều kịch bản hơn. Tất nhiên, bạn có thể tự do thực sự triển khai các hàm giả:

const myMock = jest.fn(() => {
 ...
});
//define implementations
const myMockFn = jest.fn(() => 'default return value')   
    .mockImplementationOnce(() => {
       ...
       return 'first implementation';
    })
    .mockImplementationOnce(() => {
       ...
       return 'second implementation';
    });
//call function
console.log(myMockFn(), myMockFn());
//any calls hereafter will refer to the default implementation.

Các mô-đun cũng có thể được chế tạo bằng cách sử dụng jest.mock(). Hãy nghĩ rằng các lệnh gọi cơ sở dữ liệu hoặc các yêu cầu API chậm và mỏng manh - mọi thứ có thể bị hỏng khá dễ khiến cho một bài kiểm tra không đáng tin cậy. Jest cung cấp một ví dụ đơn giản về việc giả mạo mô-đun axios để ghi đè các lệnh gọi API, nhưng đây là ý chính chung về cách mô phỏng một chức năng mô-đun:

// import the module to mock
import axios from 'axios';
// wrap the module in jest.mock()
jest.mock('axios');
test('should fetch users', () => {
  const users = [{first_name: 'Ross'}];
  const resp = {data: users};
// append .mockResolvedValue(<return value>) to the module method
  axios.get.mockResolvedValue(resp);
 // carry out your test
  return expect(resp.data).toEqual(users));
});

Gỡ lỗi các thành phần

Điều đáng nói ở đây là nếu bạn muốn ghi render()đầu ra của một thành phần trong HTML như thời trang, hãy sử dụng debug()phương thức của một trình bao bọc:

const component = shallow(<MyComponent />);
console.log(component.debug());

Tóm lược

Cùng với phần giới thiệu ban đầu của tôi về thử nghiệm , chúng tôi đã trình bày đủ trong bài nói chuyện này để đi sâu vào các bộ thử nghiệm React nghiêm túc, với một loạt các phương pháp mà khi được sử dụng cùng nhau có thể cung cấp một phương tiện toàn diện cho các thành phần thử nghiệm.

Chúng ta đã chạm vào mọi thứ chưa? Không - nhưng những công cụ mạnh mẽ này sẽ khởi động bộ thử nghiệm của bạn với Jest và Enzyme:

  • Ảnh chụp nhanh: cho phép bạn so sánh kết render()quả đầu ra của các thành phần với kết quả mong đợi.

  • Mô phỏng: thực hiện các bài kiểm tra liên quan đến các sự kiện bạn mong đợi trong trình duyệt - nhấp vào sự kiện, đầu vào biểu mẫu và gọi hàm.

  • Các hàm giả: Kiểm tra các đối số và trả về giá trị bằng các hàm giả rỗng hoặc thực hiện một loạt các triển khai để kiểm tra kết quả mong đợi. Ngoài ra, ghi đè các phương thức mô-đun dễ vỡ dựa vào các nguồn dữ liệu bên ngoài hoặc liên quan đến xử lý nặng sẽ làm chậm các thử nghiệm của bạn.

  • Khả năng trừu tượng: giữ logic cô lập và gắn liền với các thành phần cụ thể, thúc đẩy khả năng tái sử dụng, DRY và thử nghiệm tích hợp trên phạm vi rộng.

Các công cụ chúng ta đã thảo luận ở đây được sử dụng với BDD và TDD. Cá nhân tôi nghĩ rằng TDD có thể được áp dụng cho phần lớn các dự án ứng dụng, nhưng đặc biệt thích hợp cho các dự án dài hạn, dựa trên giao thức, dịch vụ API, gói quan trọng và các dự án cơ sở hạ tầng, cũng như các dự án nghiên cứu tiên tiến. Trong phát triển nhanh, có lẽ việc chọn một phương pháp cụ thể tùy thuộc vào mục tiêu nước rút của bạn sẽ có ý nghĩa đối với nhóm của bạn.

Bạn thích gì hơn - cách tiếp cận BDD để tạo bộ thử nghiệm của bạn sau khi đạt được hành vi ứng dụng mong muốn của bạn? Hoặc cách tiếp cận TDD của việc tích hợp các bài kiểm tra khi các thành phần của bạn đang được phát triển, kết nối với mục tiêu hành vi cuối cùng sau khi các thành phần của bạn được kiểm tra hoàn toàn. Cho tôi biết!

Last updated