# data[].attributes.headline
Khi có 1 hàm nào đó nhạy cảm, có chức năng quan trọng.

# data[].attributes.subject
Khi có 1 hàm nào đó nhạy cảm, có chức năng quan trọng.

# data[].attributes.headline
Muốn đảm bảo chắc chắn phần code hoạt động đúng.

# data[].attributes.subject
Muốn đảm bảo chắc chắn phần code hoạt động đúng.

# data[].attributes.body
Khi test phải SetUp môi trường, gọi API, phải login.. rất nhiều Side Effect.
Bằng việc Fake các phụ thuộc, Unit Test có thể giúp bạn test chức năng cụ thể, tạm thời loại bỏ Side effec.

# data[].attributes.body
Cần phải viết 1 bộ test case.

# data[].attributes.headline
Function phức tạp, nhận vào nhiều tham số

# data[].attributes.subject
Function phức tạp, nhận vào nhiều tham số

# data[].attributes.body
Nên viết Unit test với các trường hợp tham số khác nhau.
Ví dụ: Tham số Null, undefined,...

# data[].attributes.body
Sau này có ai sửa code thì Unit test sẽ vẫn đảm bảo chức năng hoạt động đúng.

# data[].attributes.body
Bước 1: tạo Project Vue
Bước 2: npm i @vue/cli-plugin-unit-jest
Bước 3: vue add unit-jest

npm install
npm run serve
npm run test:unit

# data[].attributes.body
Lỗi nhiều trong FrontEnd là lỗi quên check null, check undefined => Viết test case và cung cấp các loại đầu vào khác nhau. Nếu đầu vào null thì Unit test sẽ cảnh báo

# data[].attributes.headline
Config để debug trong VSCode. Thêm file Launch.json

# data[].attributes.subject
Config để debug trong VSCode. Thêm file Launch.json

# data[].attributes.body
Với debug, ta có thể:

1. Chạy 1 hoặc nhiều test suite theo ý thích.

2. Lọc ra các Test case failed

3. Run test 1 vài file theo ý thích

4. Debug trong VSCode hoặc chrome. Xem biến, xem hàm, luồng chạy,...

5. https://github.com/lovesickness111/vue-project-test/blob/main/README.md

{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Jest All Tests",
"type": "node",
"request": "launch",
"runtimeArgs": [
"--inspect-brk",
"${workspaceRoot}/node_modules/jest/bin/jest.js",
"--runInBand"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"name": "Jest watch all tests",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
"args": ["--verbose", "-i", "--no-cache", "--watchAll"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"type": "node",
"request": "launch",
"name": "Jest watch current file",
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
"args": [
"NewTask",
"--verbose",
"-i",
"--no-cache",
"--watchAll"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}

# data[].attributes.attachment_link.description
Việc xác định tại sao phải test và test cái gì rất quan trọng. Đầu tiên chúng ta cần phải xác định khi nào viết Unit test cho FrontEnd. **** Khi có 1 hàm nào đó nhạy cảm, có chức năng quan trọng và bạn nhận thấy cần biết chắc nó hoạt động trơn tru.

# data[].attributes.headline
Sau khi SetUp, Jest có thể hiểu tất cả các File có extension "spec.js hoặc spec.ts" trong thư mục "/tests/".

# data[].attributes.subject
Sau khi SetUp, Jest có thể hiểu tất cả các File có extension "spec.js hoặc spec.ts" trong thư mục "/tests/".

# data[].attributes.headline
Mỗi file test (file .spec.js) chỉ nên dành cho test 1 component.

# data[].attributes.subject
Mỗi file test (file .spec.js) chỉ nên dành cho test 1 component.

# data[].attributes.body
1. import { mount, shallowMount } from "@vue/test-utils"; => mount, shallowMount là 2 hàm quan trọng dùng để tạo ra instance của Vue Component

2. Mount => tạo ra component với giống vue bình thường, render cả component con (nếu có)

3. shallowMount => tạo ra DUY NHẤT component chỉ định, không render component con để tránh những ảnh hưởng không muốn test

# data[].attributes.body
VD: const wrapper = mount(NewTask);

wrapper hiện tại là 1 đại diện của NewTask.Vue => Ta có thể truy cập tất cả thuộc tính của Component NewTask.Vue

wrapper.vm cho phép ta truy cập được vào data, props, methods,... của Component

wrapper.html() sẽ trả về chuỗi html khi component được render ra

const input = wrapper.find("input.input-task"); => tìm input có class .input-task để thao tác với input

input.setValue("write unit test"); => Set tên task mới là : write unit test

wrapper.find("button").trigger("click") => gọi vào sự kiện click của button

Tại sao phải có Await, NextTick? => vì những luồng thao tác này bất đồng bộ nên cần phải chờ thao tác thực hiện xong thì ta mới thực thi code tiếp

expect => Mong chờ 1 kết quả nào đó. => Nếu (expect = true) thì test case sẽ passed, ngược là thì test case sẽ failed

# data[].attributes.body
(1) tạo ra wrapper bằng cách mount hoặc shallowMount

(2) truy cập data, props, function trong Component

(3) thay đối giá trị data, props, function trong Component

(4) find element trên DOM. Check nó có tồn tại hoặc chứa attribute không?

(5) trigger events trên DOM, ví dụ như click vào 1 element

(6) check Component có chứa html element hay không? (gần giống ý 4)

(7) Fake các phụ thuộc (component con, API, methods) để test unit

# data[].attributes.body
describe: được sử dụng để nhóm test cases và mô tả hành vi của functions/modules/class.
Nó nhận hai tham số, tham số đầu tiên mô tả "nhóm test cases của bạn" và có kiểu dữ liệu là string.
Tham số thứ hai là một function callback trong đó bạn có test case hoặc hook functions.

# data[].attributes.body
Đây là test case, đơn vị test của bạn. Nó phải được mô tả, và các tham số phải chính xác như describe.

# data[].attributes.body
hook function chạy trước (sau) tất cả tests. Nó nhận một tham số và function của bạn chạy trước (sau) tất cả tests.

# data[].attributes.body
hook function chạy trước (sau) mỗi tests. Nó nhận một tham số và function của bạn chạy trước (sau) mỗi tests.

# data[].attributes.body
Để đưa ra khẳng định, bạn cần một đầu vào và đầu ra dự kiến

expect(input).matcher(output)

# data[].attributes.body
Có thể bỏ qua test bằng cách sử dụng .skip trong describe và it: describe.skip(...) hay it.skip(...).

Có thể chạy riêng test mà bạn muốn bằng cách sử dụng .only trong describe hay it: describe.only(...) hay it.only(...).

# data[].attributes.body
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

test('null', () => {
  const n = null;
  expect(n).toBeNull();
  expect(n).toBeDefined();
  expect(n).not.toBeUndefined();
  expect(n).not.toBeTruthy();
  expect(n).toBeFalsy();
});

test('zero', () => {
  const z = 0;
  expect(z).not.toBeNull();
  expect(z).toBeDefined();
  expect(z).not.toBeUndefined();
  expect(z).not.toBeTruthy();
  expect(z).toBeFalsy();
});

# data[].attributes.body
toContain

Kiểm tra 1 mảng, chuỗi có chứa 1 tham số được truyền vào

const alphabets = [
  'a',
  'b',
  'c',
  'xxx',
  'yyy',
];

test('alphabets co chua xxx', () => {
  expect(alphabets).toContain('xxx');
});

toBe
So sánh các giá trị nguyên thủy (boolean, number, string) hoặc các tham chiếu của objects và arrays:

expect(add(2, 2)).toBe(4);

isEqual
So sánh tất cả thuộc tính của objects, arrays:

const add = (a, b) => ({ a, b });
test('check obj', () => {
  expect(add(2, 2)).toEqual({ a: 2, b: 2 });
});

toStrictEqual

Sử dụng toStrictEqual được ưa thích hơn sử dụng toEqual. toEqual đơn giản bỏ qua các giá trị không xác định, trong khi toStrictEqual tính đến chúng.

toBeCloseTo
So sánh số thực với dấu chấm độ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ỏ.

toThrow
Kiểm tả nếu function bắn ra lỗi:

function divideByZero() {
  throw new Error('Ban khong duoc phep chia cho 0');
}

test('Thuc thi ham divideByZero bi loi', () => {
  expect(() => divideByZero()).toThrow();
  expect(() => divideByZero()).toThrow(Error);

  // Hoặc bạn có thể so sánh chính xác message lỗi hoặc dùng regex
  expect(() => divideByZero()).toThrow('Ban khong duoc phep chia cho 0');
  expect(() => divideByZero()).toThrow(/khong/);
});

Hàm throw một exception cần phải được gọi trong một hàm khác ở expect. Nếu không, quá trình toThrow sẽ không được thành công.

# data[].attributes.body
Phải được đặt trước một matcher và trả về kết quả ngược lại với kết quả của matcher:

# data[].attributes.body
Kiểm tra hàm được gọi ít nhất 1 lần

# data[].attributes.body
Test xem với giá trị truyền vào của Props hoặc giá trị khởi tạo của Data thì Code có chạy đúng dự định không?

# data[].attributes.body
B1: mount component với option truyển vào (prop hoặc data)

B2: Kiểm tra với (prop hoặc data) truyền vào thì component có nhận được và render theo (prop hoặc data) đúng hay không?

# data[].attributes.headline
Có 3 cách Set giá trị cho (prop hoặc data)

# data[].attributes.subject
Có 3 cách Set giá trị cho (prop hoặc data)

# data[].attributes.body
Cách 1: set cùng hàm mount/shallowMount:
const wrapper = shallowMount(
HelloWorld, {
 propsData: { msg: "new message" },
});

Cách 2: Dùng hàm (setProps hoặc setData) ví dụ:
await wrapper.setProps({ msg: "new message" });

Cách 3: Set thẳng giá trị vào Props hoặc Data (ví dụ:
wrapper.vm.propName = "giá trị A")
await nextTick();
//Khi await vào dòng code không phải hàm thì dùng nextTick(). Cần import from vue
//Sẽ không dùng cách này vì trong vòng đời Vue không nên thay đổi Props

# data[].attributes.body
Theo dõi các hàm hoặc method trong component

spy = jest.spyOn(console, "log"); // theo dõi sự kiện console.log
const spy = jest.spyOn(wrapper.vm,"addNewTask")

afterEach(() => {        spy.mockClear();    });//bỏ theo dõi, giải phóng bộ nhớ

# data[].attributes.body
await cmp.setData({ inputValue: "foo" });
expect(spy).not.toBeCalled();

# data[].attributes.body
beforeAll trước khi tất cả các test case được chạy, và chạy 1 lần

# data[].attributes.body
Trước khi 1 test case chạy thì sẽ chạy vào beforeEach và dùng khi việc kiểm tra cần lặp đi lặp lại nhiều lần.

# data[].attributes.headline
Lưu ý: Thường xuyên nghĩ đến Async Await vì Js sẽ chạy code không đồng bộ

# data[].attributes.subject
Lưu ý: Thường xuyên nghĩ đến Async Await vì Js sẽ chạy code không đồng bộ

# data[].attributes.body
A> Test hàm có được gọi hay không?
B> Test hàm có thực thi đúng hay không? (có 1 đầu vào => expect đầu ra tương ứng)
C> Fake hàm trong file test, overriding lại hàm trong test

# data[].attributes.body
Để test hàm có được gọi thành công hay không, chúng ta cần làm 3 bước:

1. Dùng Jest.spyOn(functionA) để theo dõi functionA.

2. Trigger gọi functionA. Bạn có thể trigger bằng sự kiện trên DOM hoặc chỉ đơn giản gọi wrapper.vm.functionA()

3. Assert, sử dụng hàm toBeCalled của jest: expect(wrapper.vm.functionA).toBeCalled();

4. Lưu ý

1. Mỗi lần ta lại cung cấp đầu vào (INPUT) khác nhau.

2.  Để test hàm xem có thực thi đúng hay không, chúng ta làm lần lượt 3 bước trên

3. Dùng jest.fn(_funtion_fake) để làm giả function trong component. => Xem ví dụ 3 trong file mẫu

# data[].attributes.body
await wrapper.find("button").trigger("click"); //lần đầu emit ra giá trị new task        wrapper.vm.addNewTask();
wrapper.vm tương tự như con trỏ this ở component

# data[].attributes.headline
fake function nào đó bắt buộc phải trả về giá trị nào đó để test case liền sau

# data[].attributes.subject
fake function nào đó bắt buộc phải trả về giá trị nào đó để test case liền sau

# data[].attributes.body
wrapper.vm.checkIsNullTask = jest.fn(() => {
return false;
});

# data[].attributes.body
Kiểm tra từng đơn vị code
Thấp hơn Automation Test

# data[].attributes.body
Viết và chạy khi thi công chức năng
Kiểm tra lại ảnh hưởng khi có sự thay đổi code

# data[].attributes.body
Tốc độ nhanh
Dễ viết, viết ngay trong quá trình thực hiện code

# data[].attributes.body
Unit Test triển khai cho các chức năng mới phát triển, khả năng phát hiện lỗi sớm
Cover các trường hợp lỗi và khoanh vùng được lỗi
Cần define được các case cần test

Auto kiểm tra những chức năng tư trước đã release rồi để đánh giá ảnh hưởng các phần mới làm
Run Auto sau mỗi bản build local. Khi phiên bản build ở các môi trường thì sẽ Run Test
Run trên nhiều máy
1 ngày viết được 1 - 2 script

# data[].attributes.body
Là những trường hợp kiểm thử đảm bảo người dùng có thể thực hiện được thao tác với dữ liệu hợp lệ.

# data[].attributes.body
Là những trường hợp kiểm thử tìm cách gây lỗi cho ứng dụng bằng cách sử dụng các dữ liệu không hợp lệ.

# data[].attributes.body
Arrange – Chuẩn bị dữ liệu đầu vào và các điều kiện khác để thực thi test case.

Act – Thực hiện việc gọi phương thức/hàm với đầu vào đã được chuẩn bị ở Arrange và nhận về kết quả thực tế.

Assert – So sánh giá trị mong đợi và giá trị thực tế nhận được ở bước Act. Kết quả của test case sẽ là một trong hai trạng thái sau:

PASS: nếu kết quả mong đợi và kết quả thực tế khớp nhau

FAIL: nếu kết quả mong đợi khác với kết quả thực tế

# data[].attributes.body
Dễ viết – Có thể bao quát được nhiều trường hợp kiểm thử mà không mất quá nhiều công sức.

Dễ đọc – Có thể mô tả được chính xác hành vi hoặc chức năng được kiểm thử.

Tự động hoá – Có thể thực thi lặp lại nhiều lần.

Dễ thực thi và thực thi nhanh.

Đồng nhất – Luôn trả về cùng kết quả sau mỗi lần chạy (nếu không thay đổi mã nguồn bên trong).

Cô lập – Có thể thực thi độc lập mà không phụ thuộc vào các thành phần khác trong hệ thống. Bạn có thể tham khảo mục “Sử dụng Mockito” để làm rõ hơn ý này.

Khi kết quả kiểm thử thất bại (FAILED), có thể dễ dàng tìm ra giá trị mong đợi và nhanh chóng xác định được vấn đề.

# data[].attributes.body
return fetchData().then(data => {...})

# data[].attributes.body
test('foo', async () => {
  const data = await fetchData();
  expect(data).toBe('foo');
});

# data[].attributes.body
Sử dụng jest.useFakeTimers() ngay ở đầu test file. Sau đó sử dụng jest.runAllTimers() để lướt nhanh cho đến khi toàn bộ timers được thực thi.