[JEST] Test component trong React với Jest

https://viblo.asia/p/test-component-trong-react-voi-jest-maGK77WaKj2

Một số ví dụ lấy từ trang chủ của jest https://jestjs.io/docs/en/using-matchers

Using Matchers

Cách đơn giản nhất để kiểm tra một giá trị là bằng chính xác.

test('two plus two is four', () => {
  expect(2 + 2).toBe(4);
});

toBe sử dụng Object.is để kiểm tra sự bình đẳng chính xác. Nếu bạn muốn kiểm tra giá trị của một đối tượng, hãy sử dụng toEqual để thay thế. toEqual recursively checks every field of an object or array.

test('object assignment', () => {
  const data = {one: 1};
  data['two'] = 2;
  expect(data).toEqual({one: 1, two: 2});
});

Bạn cũng có thể kiểm tra điều ngược lại của trình so khớp:

test('adding positive numbers is not zero', () => {
  for (let a = 1; a < 10; a++) {
    for (let b = 1; b < 10; b++) {
      expect(a + b).not.toBe(0);
    }
  }
});

Trong các bài kiểm tra, đôi khi bạn cần phân biệt giữa không xác định, null và sai, nhưng đôi khi bạn không muốn xử lý chúng theo cách khác nhau. Jest chứa những người trợ giúp cho phép bạn rõ ràng về những gì bạn muốn.

toBeNull chỉ đối sánh với null
toBeUndefined chỉ đối sánh không xác định
toBeDefined đối lập với toBeUndefined
toBeTruthy khớp với bất kỳ thứ gì mà câu lệnh if coi là true
toBeFalsy khớp với bất kỳ thứ gì mà câu lệnh if coi là sai

Numbers

Hầu hết các cách so sánh các số đều có tương đương đối sánh.

test('two plus two', () => {
  const value = 2 + 2;
  expect(value).toBeGreaterThan(3);
  expect(value).toBeGreaterThanOrEqual(3.5);
  expect(value).toBeLessThan(5);
  expect(value).toBeLessThanOrEqual(4.5);
  // toBe and toEqual are equivalent for numbers
  expect(value).toBe(4);
  expect(value).toEqual(4);
});

Đối với bình đẳng dấu phẩy động, hãy sử dụng toBeCloseTo thay vì toEqual, vì bạn không muốn kiểm tra phụ thuộc vào một lỗi làm tròn nhỏ.

test('adding floating point numbers', () => {
  const value = 0.1 + 0.2;
  //expect(value).toBe(0.3);           This won't work because of rounding error
  expect(value).toBeCloseTo(0.3); // This works.
});

Strings

Bạn có thể kiểm tra chuỗi so với biểu thức chính quy bằng toMatch:

test('there is no I in team', () => {
  expect('team').not.toMatch(/I/);
});
test('but there is a "stop" in Christoph', () => {
  expect('Christoph').toMatch(/stop/);
});

Arrays and iterables

Bạn có thể kiểm tra xem một mảng hoặc có thể lặp lại có chứa một mục cụ thể hay không bằng cách sử dụng toContain:

const shoppingList = [
  'diapers',
  'kleenex',
  'trash bags',
  'paper towels',
  'beer',
];

test('the shopping list has beer on it', () => {
  expect(shoppingList).toContain('beer');
  expect(new Set(shoppingList)).toContain('beer');
});

Exceptions

Nếu bạn muốn kiểm tra xem một hàm cụ thể có phát ra lỗi khi nó được gọi hay không, hãy sử dụng toThrow.

function compileAndroidCode() {
  throw new Error('you are using the wrong JDK');
}
test('compiling android goes as expected', () => {
  expect(() => compileAndroidCode()).toThrow();
  expect(() => compileAndroidCode()).toThrow(Error);
  // You can also use the exact error message or a regexp
  expect(() => compileAndroidCode()).toThrow('you are using the wrong JDK');
  expect(() => compileAndroidCode()).toThrow(/JDK/);
});

Một ví dụ Test react component được viết theo phiên bản "@testing-library/jest-dom": "^5.11.4"

Đầu tiên ta sẽ viết vài case test cho ProductHeader. src/ProductHeader.js

C:\Users\Administrator\Desktop\test\src_test_\ProductList.test.js

import React from 'react';
import ReactTestUtils from 'react-dom/test-utils';
import ProductHeader from '../ProductHeader';
test('ProductHeader Component', () => {
  const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);    
  var h2 = ReactTestUtils.findRenderedDOMComponentWithTag(
   	component, 'h2'
 	);
})

C:\Users\Administrator\Desktop\test\src\ProductHeader.js

import React, { Component } from 'react';
class ProductHeader extends Component {
  render() {
    return (
      <h3 className="title"> Product Listing Page </h3>
    );
  }
};
export default ProductHeader;

Để test ta có hai giả định như sau :

  1. Component này sẽ trả về h2tag.

  2. h2tag sẽ có một lớp tên title.

Quay lại file test ta viết như sau :

C:\Users\Administrator\Desktop\test\src_test_\ProductList.test.js

import React from 'react';
import ReactTestUtils from 'react-dom/test-utils';
import ProductHeader from '../ProductHeader';
test('ProductHeader Component', () => {
  const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);    
  var h2 = ReactTestUtils.findRenderedDOMComponentWithTag(
   	component, 'h2'
 	);
})

Để test được đầu tiên ta phải render vào trong document. Với sự giúp đỡ của ReactTestUtils, hãy viết như sau :

const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);

Sau đó sửa lại hàm test như sau :

import React from 'react';
import ReactTestUtils from 'react-dom/test-utils';
import ProductHeader from '../ProductHeader';
test('Has an h2 tag', () => {
  const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);
  var h2 = ReactTestUtils.findRenderedDOMComponentWithTag(
    component, 'h2'
  );
});

Tương tự như vậy với case test thứ hai

import React from 'react';
import ReactTestUtils from 'react-dom/test-utils';
import ProductHeader from '../ProductHeader';
test('Has a title class', () => {
  const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);
  var node = ReactTestUtils.findRenderedDOMComponentWithClass(
    component, 'title'
  );
})

Chú ý: Cách setup jest.config.js

C:\Users\Administrator\Desktop\exampleforked\jest.config.js

const path = require("path");
module.exports = {
  roots: [path.resolve(__dirname, "../src")],
  testEnvironment: "jest-environment-jsdom-sixteen",
  displayName: "local",
  testURL: "http://localhost",
  setupFilesAfterEnv: [path.resolve(__dirname, "./src/setupTests.ts")],
  transform: {
    "^.+\\.(ts|tsx)$": "ts-jest"
  }
};

C:\Users\Administrator\Desktop\exampleforked\src\setupTests.ts

import "@testing-library/jest-dom/extend-expect";

Trong phiên bản mới có sự thay đổi

C:\Users\Administrator\Desktop\reactrouter\src\setupTests.js

import '@testing-library/jest-dom';

Test component trong React với Jest

Testing code luôn là bài toán khó khắn với mỗi developer, bởi vì đơn giản là nó tốn nhiều thời gian, công sức, thậm chí bạn phải có khả năng nhìn thấy được những trường hợp có thể xảy ra. Thường những project nhỏ sẽ bỏ qua công đoạn này bởi vì thiếu nguồn lực. Tuy nhiên, nó vẫn luôn là cần thiết bởi nó sẽ giúp bạn cảm thấy tự tin hơn với dòng code của mình, tăng năng suất lao động.

Trong React cũng phải khác biệt gì, và ở trong bài viết này tôi sẽ hướng dẫn các bạn cách viết test cho React component.

Test component trong React

Test là quá trình kiểm tra xem những điều kiện mà chúng ta đặt ra có được trả về chính xác không thông qua quá trình hoạt động của ứng dụng. Có rất nhiều kiểu test cho những lập trình viên để nhưng ở đây tôi chỉ nói đến những mô hình test phổ biến mà hay được sử dụng đó là : unit test, functional test và integration test.

Unit test

Unit test đã là một kiểu test quá cơ bản với mỗi quy trình test. Như cái tên của nó, bạn sẽ phải test từng phần code riêng với những chức năng mà chúng phụ trách. Bởi vì kết cấu của các Component trong React nên unit test cực kỳ hữu ích. Unit test giúp bạn phân chia các component thành các chức năng riêng biệt. Phần unit test của bạn trong component phải trả lời được các câu hỏi sau:

  1. Có prop nào không? Nếu có thì để làm gì ?

  2. Nó render ra component nào ?

  3. Nó có nên có state không ? Khi nào và như thế nào nên update state ?

  4. Có quá trình nào mà nó phải theo dõi khi được mount và unmount hoặc người dùng tương tác ?

Functional test

Functional test được sử dụng để test việc đối ứng của một phần của hệ thống. Function test thường được viết bởi góc nhìn của người dùng. Một phần của chức năng không chỉ giới hạn bởi một component.

Integration test

Integration test là quá trình gộp các component riêng biệt lại thành một nhóm. Nó nhằm kiểm tra quá trình hoạt động của user, tuy nó lâu hơn functional test và unit test nhưng nó được thực hiện trực tiếp. Trong React, unit test và functional test nổi tiếng hơn bởi vì chúng dễ viết và bảo trì.

Những Tool sẽ sử dụng

Jest Test

Jest là một framework test mà không cần phải tuỳ chỉnh gì cả và rất dễ để cài đặt. Nó nổi tiếng hơn những framework như là Jasmine và Mocha bởi nó được phát triển bởi Facebook. Jest thậm chí còn nhanh hơn chúng bởi vì nó sử dụng kỹ thuật chạy test song song trong mỗi worker. Ngoài ra, mỗi lần chạy test được đưa vào trong môi trường sandbox riêng để tránh conflict với các case test khác.

Nếu bạn dùng create-react-app thì nó đã có cả Jest. Nếu không, bạn sẽ phải cài Jesst và một vài dependency khác. Hãy tham khảo tài liệu Jest ở đây.

react-test-renderer

Cho dù bạn có dùng create-react-app thì bạn vẫn phải cài phần này vì nó sẽ render snapshot. Snapshot test là một phần của Jest. Thay vì phải render UI của cả ứng dụng thì ta có thể dùng test render để tạo ra output được giả lập.

yarn add react-test-renderer

ReactTestUtils và Enzyme

react-dom/test-utils chứa những phần tuỳ chỉnh test thuộc về React. Ngoài ra bạn có thể sử dụng Enzyme của Airbnb. Enzyme tốt hơn ReactTestUtils bởi vì nó dễ sử dụng, truy ngược về output của React Component. Chúng ta sẽ bắt đầu bằng React util sau đó sẽ chuyển sang Enzyme.

Để cài Enzyme :

yarn add enzyme enzyme-adapter-react-16

Thêm code sau vào src/SetupTest.js

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
 
configure({ adapter: new Adapter() });

Cài đặt Demo App và sắp xếp Test

Chúng ta sẽ viết một ứng dụng demo có chức năng hiện lên danh sách sản phẩm. Ứng dụng chứa các component như là ProudctContainer.js và 3 present component là ProductList,ProductDetails, và ProductHeader.

.
├── package-lock.json
├── package.json
├── public
│   ├── index.html
│   └── manifest.json
├── src
│   ├── components
│   │   ├── App.js
│   │   ├── ProductContainer.js
│   │   ├── ProductDetails.jsx
│   │   ├── ProductHeader.js
│   │   ├── ProductList.jsx
│   ├── index.js
│   └── style.css

Bạn có thể lấy demo ở đây. Khi bạn download về, hãy tạo thư mục là __test__ ở trong /src/components/.

Viết Test trong React

Tạo file ProductHeader.test.js nếu bạn chưa có. Đây là ví dụ về việc test sẽ viết như thế nào

src/components/__tests__/ProductList.test.js

describe('ProductHeader', () => {
 
  it('passing test', () => {
    expect(true).toBeTruthy();
  })
 
  it('failing test', () => {
    expect(false).toBeTruthy();
  })
})

Việc test bắt đầu với describe block, nó là function của Jest với hai parameter. Cái đầu là tên của cast test, cái thức hai là phần muốn test. Từ khoá it() thể một test case hoặc một spec. Mỗi trường hợp test có chức một hoặc nhiều kết quả test mà kiểm tra tình trạng của code.

expects(true).toBeTruthy();

toBeTruthy() là một hàm so sánh được define sẵn. Trong Jest, mỗi hàm so sánh tạo bước so sánh với giá trị kết quả và thực tế và trả về giá trị boolean.

Chạy test

create-react-app đã cài đặt tất cả nên bạn chỉ cần chạy test bằng câu lệnh sau:

yarn test

Bạn thay hàm toBeTruthy() bằng toBeFalsy() để pass test.

Các hàm so sánh trong Jest

Như đã nêu ở trên, Jest có rất nhiều hàm so sánh như sau:

toBe()
toBeNull()
toBeDefined()
toBeUndefined()
toBeTruthy()
toBeFalsy()
toBeGreaterThan()
toBeLesserThan()
toMatch()
toContain()

Ngoài ra bạn có thể tham khảo tài liệu sau để tìm nhiều hàm hơn : https://jestjs.io/docs/en/using-matchers

Test react component

Đầu tiên ta sẽ viết vài case test cho ProductHeader. src/components/ProductHeader.js

import React, {Component} from 'react';
    
class ProductHeader extends Component  {
    render() {
        return(
            <h2 className="title"> Product Listing Page </h2>
        );
    }
};
export default ProductHeader;

Để test ta có hai giả định như sau :

  1. Component này sẽ trả về h2tag.

  2. h2tag sẽ có một lớp tên title.

Quay lại file test ta viết như sau :

src/components/__tests__/ProductHeader.test.js

import React from 'react';
import ReactTestUtils from 'react-dom/test-utils'; 
import ProductsList from '../ProductsList';
 
describe('ProductHeader Component', () => {
 
    it('has an h2 tag', () => {
     //Test here
    });
   
    it('is wrapped inside a title class', () => {
     //Test here
    })
  })

Để test được đầu tiên ta phải render vào trong document. Với sự giúp đỡ của ReactTestUtils, hãy viết như sau :

const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);

Sau đó sửa lại hàm test như sau :

  it('has an h2 tag', () => {
 
    const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);    
    var h2 = ReactTestUtils.findRenderedDOMComponentWithTag(
     component, 'h2'
   );
   
});

Tương tự như vậy với case test thứ hai

it('has a title class', () => {
 
  const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);    
  var node = ReactTestUtils.findRenderedDOMComponentWithClass(
   component, 'title'
 );
})

Sau đó xem kết quả:

Vậy qua bài này chúng ta đã viết được case test trên React, ở phần sau ta sẽ dùng Enzyme để viết bởi vì Enzyme dễ dàng phát triển hơn, dễ dùng hơn. Cảm ơn các bạn đã xem.

Last updated

Was this helpful?