11/08/2018, 20:13

Học React/Redux qua ví dụ thực tế: Testing

Chào mừng các bạn trở lại với series Học React/Redux qua ví dụ thực tế, trong bài trước chúng ta đã setup xong project và đã viết được những React component đầu tiên. Trong bài học lần này tôi sẽ hướng dẫn các bạn các setup để test các React component. Tôi sẽ hướng dẫn testing một cách cơ bản mà ...

Chào mừng các bạn trở lại với series Học React/Redux qua ví dụ thực tế, trong bài trước chúng ta đã setup xong project và đã viết được những React component đầu tiên. Trong bài học lần này tôi sẽ hướng dẫn các bạn các setup để test các React component. Tôi sẽ hướng dẫn testing một cách cơ bản mà không đi quá sâu vào chủ để testing đâu nhé!

Để test, chúng ta sẽ sử dụng mocha, một test framework, chai môt thư viện dùng để so sánh, và jsdom một thư viện cung cấp cho chúng ta những tính năng truy xuất vào DOM trên node.

Từ thư mục gốc của project các bạn cài đặt như sau.

npm install --save-dev mocha chai jsdom

Tiếp đến chúng ta cần một file setup để chứa các config. Từ thư mục gốc các bạn tạo file test setup như sau.

mkdir test
cd test
touch setup.js

Mở file setup.js và bắt đầu cài đặt môi trường test thôi.

import React from 'react';
import {expect} from 'chai';
import jsdom from 'jsdom';

const document = jsdom.jsdom('<!doctype html><html><body></body></html>');
const window = document.defaultView;

global.document = document;
global.window = window;

Object.keys(window).forEach((key) => {
  if (!(key in global)) {
    global[key] = window[key];
  }
});

global.React = React;
global.expect = expect;

Về cơ bản, chúng ta expose một jsdom document đã được generate và một window object ra global scope, những thứ này sẽ được sử dụng bởi React trong quá trình test. Ngoài ra chúng ta cũng cần phải expose tất cả các thuộc tính của window object để chúng có thể được sử dụng sau. Cuối cùng nhưng cũng không kém phần quan trọng đó là cho phép những object React cũng như expect được truy xuất ở global scope. Điều này giúp chúng ta không phải tốn công import chúng vào mỗi khi muốn test.

Trong file package.json chúng ta sẽ thêm một script mới để chạy test, script này sẽ dùng mocha như là test framework, sửu dụng file test/setup.js để cài đặt môi trường và duyệt qua tất cả các file có phần đuôi dạng *.spec.js trong thư mục src như là những test files.

...
  "scripts": {
    ...
    "test": "mocha --compilers js:babel-core/register --require ./test/setup.js 'src/**/*spec.js'"
  },
...

Ngoài ra còn chúng ta còn cần một số thư viên gọn gàng hơn để giúp chúng ta test các React component. Enzyme là một thư viện hỗ trợ chúng ta giả lập trạng thái tại thời điểm test React component. Nào cùng cài đặt nó!

npm install --save-dev react-addons-test-utils enzyme

Bây giờ thì còn chờ gì nữa, bắt tay vào viết test file đầu tiên cho React component của chúng ta nào.

Trong thư mục src/components tạo file TrackList.spec.js để test component TrackList. Cùng viết các test case cho component trong file này.

import TrackList from './TrackList';
import {shallow} from 'enzyme';

describe('TrackList', () => {

  it('shows two tracks', () => {
    const props = {
      tracks: [{ id: 1, title: 'foo' }, { id: 2, title: 'bar' }]
    };
    const element = shallow(<TrackList {...props} />);

    expect(element.find('div')).to.have.length(2);
  });

});

Đây là test case tôi viết để test component TrackList. Như chúng ta đã biết thì khi component này sẽ render ra các div chứa title của các bài hát, ở đây tôi truyền vào props là danh sách hai bài hát vì thế nó sẽ render ra hai div chứa tên bài hát. Khi run test, nó sẽ phải pass.

Cùng run test bằng lệnh npm test và xem kết quả nào.

Ơ, hình như có gì đó sai sai, test chạy fail, nào cùng double check lại nhé, test case chúng ta đưa ra là truyền vào danh sách hai bài hát thì render ra hai div, sao sai nhỉ?

Ngó lại component TrackList , à thì ra là nó còn được wrap lại bởi một div ở bên ngoài. Thay vì tìm div thì ta phải tìm div con của div. Vì thế test case sẽ được sửa lại như sau.

import TrackList from './TrackList';
import {shallow} from 'enzyme';

describe('TrackList', () => {

  it('shows two tracks', () => {
    const props = {
      tracks: [{ id: 1, title: 'foo' }, { id: 2, title: 'bar' }]
    };
    const element = shallow(<TrackList {...props} />);

    expect(element.find('div > div')).to.have.length(2);
  });

});

Chạy test lại nào.

Tuyệt vời, test pass rồi. Thật ra tôi cố tình viết test fail để cho các bạn thấy mặt mũi cái test fail nó ra thế nào thôi, chứ không phải tui code lởm đâu nhé, haha.

Được đà viết thêm cái test case nữa nào, lần này chúng ta sẽ test xem component có render ra đúng tên bài hát hay không.

import TrackList from './TrackList';
import {shallow} from 'enzyme';

describe('TrackList', () => {

  ...

  it('shows track title', () => {
    const props = {
      tracks: [{ id: 1, title: 'foo' }]
    }
    const element = shallow(<TrackList {...props} />);

    expect(element.contains('foo')).to.be.true;
  })

});

Chạy test nào, lần này bao pass nhe, tôi tính toán hết rồi.

Ngoài ra, chúng ta có thể thêm script vào package.json để watch xem trong quá trình develop ở local, bất cứ chỉnh sửa nào làm test fail ta cũng có thể nhìn thấy.

...
  "scripts": {
    ...
    "test:watch": "npm run test -- --watch"
  },
...

Để chạy, đơn giản nhập vào termial lệnh npm run test:watch.

Source code trong bài các bạn có thể tìm thấy ở https://github.com/codeaholicguy/react-redux-tutorial/tree/master/testing-setup

Hôm nay đến đây là đủ rồi, tôi sẽ không viết thêm test case nào nữa đâu. Tự do thoải mái sáng tạo thêm test case, nghịch chán chê đi để đợi bài sau nhé, bài sau chúng ta sẽ đụng tới Redux đấy! Có comment gì đừng quên để lại cho tôi phía bên dưới bài viết nhé, tạm biệt!

Bài gốc: Codeaholicguy

0