Bài viết gồm 3 phần
- Phần 1: Làm quen với puppeteer
- Phần 2: Dùng puppeteer để cào dữ liệu và vếu từ mương14.
- Phụ lục: Tổng quan về testing và automation test
- Phần 3: Viết Automation test với Puppeteer
Trong phần này, mình sẽ hướng dẫn các bạn viết automation test trong NodeJS với Puppeteer và Jest. Chúng ta cùng thử viết test tìm kiếm hàng hoá trên lazada nhé.
Kiến thức của phần này tuy cô đọng, ngắn gọn nhưng cần khá nhiều kiến thức nền nên các bạn đọc lại những bài này để nhớ lại kiến thức lại nhé!
- Tổng quan về testing, unit test và automation test
- Viết Unit Test với C# (Giải thích vai trò của unit test trong việc làm code tốt hơn)
- Viết Unit Test với Jasmine (Giải thích các khái niệm hay gặp khi viết unit test)
Selenium và Puppeteer
Với các dự án viết bằng C#, Java, người ta thường chọn JUnit, NUnit để làm testing framework (viết test case, chạy test case, báo cáo tỉ lệ pass/fail).
Đi kèm với chúng thường là Selenium – một thư viện khá mạnh cho phép ta điều khiển trình duyệt Chrome, Firefox, IE.
So với Selenium thì Puppeteer vẫn còn khá thua kém vì thiếu một số tính năng (Chưa chạy được đồng thời trên nhiều máy, chưa select được xPath) và chỉ có thể chạy được trên trình duyệt Chrome.

Tuy vậy, nó có chút ưu điểm là do chính team của Google phát triển, dễ cài đặt, cài xong là chạy ngay, còn có thể chạy headless không cần giao diện nên tốc độ chạy sẽ nhanh hơn.
Do vậy, bạn có thể làm theo bài viết để tìm hiểu về cách viết code cho Automation Testing trước. Nếu xác định thực sự muốn theo ngành này thì nên nhớ đầu tư thời gian để học Selenium nhé.
Cài đặt project automation test
Do Puppeteer chạy trên nền NodeJS nên chúng ta sẽ dùng Jest testing framework.
Đây là một testing framework gọn nhẹ, mạnh mẽ, tiện dụng do Facebook chống lưng. Các bạn tìm hiểu thêm về Jest tại đây nhé!
Một trong số các ưu điểm của Jest là dễ cài đặt, cài phát là chạy ngay được luôn, chúng ta cùng làm theo 4 bước sau để khởi tạo project.
- Tạo thư mục mới đặt tên là puppet-test
- Mở cmd rồi cd vào thư mục này. Lần lượt gõ các lệnh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
npm init | |
npm install –save jest | |
npm install –save puppeteer |
3. Mở file packages.json, sửa nội dung script test thành jest như hình dưới:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "puppet-test", | |
"version": "1.0.0", | |
"description": "", | |
"main": "index.js", | |
"scripts": { | |
"test": "jest –verbose true" | |
}, | |
"author": "", | |
"license": "ISC", | |
"dependencies": { | |
"jest": "^22.0.4", | |
"puppeteer": "git+https://github.com/GoogleChrome/puppeteer.git" | |
} | |
} |
Vậy là bạn đã cài đặt xong rồi đấy!
Mặc định, Jest sẽ chạy unit test trong những file có đuôi .spec.js hoặc .test.js. Do vậy, chúng ta tạo một file tên number.test.js và code đoạn code sau.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test('One plus one equal 2', async() => { | |
expect(1 + 1).toBe(2); | |
}); |
Sau đó, trong cửa sổ cmd, ta gõ npm run test để chạy unit test. Bạn sẽ thấy kết quả như sau.
Ok, vậy là mọi thứ đã sẵn sàng rồi đấy. Bắt đầu viết automation test thôi nào.
Một số API cần dùng khi viết automation test
Mà khoan đã, trước khi cắm đầu vào code thì phải biết chúng ta nên code cái gì, code ra sao đã chứ!
Như mình đã nói ở bài trước, automation test tức là ta tự động hoá những việc mình làm lúc manual test. Vậy lúc test kiểu thủ công, bạn làm những gì?
Phần lớn những việc bạn làm sẽ là:
- Đăng nhập
- Nhập thông tin vào form này form kia
- Click chuột chỗ này chỗ nọ
- Sau đó kiểm tra kết quả
Do vậy, đây là những API của pupetter mà chúng ta sẽ dùng để làm những việc trên:
Hai hàm này tìm element trong page dựa vào css selector hoặc xpath
- page.$(selector): Dùng CSS select để select một element trên trang.
- page.$x(expression): Dùng xpath để select element trên trang. Cái này các bạn tester thích lắm, vì nhiều khi mấy đứa code không có class hay ID gì cả, dùng xpath mới tiện.
- Hàm này trả về 1 array chứa các element nên nếu chỉ có 1 element, các bạn check phần tử đầu tiên trong array nhé.
Sau khi tìm được element (link, textbox, button), ta dùng các hàm sau để điền thông tin, click v…v:
- elementHandle.type(text): Điền thông tin vào textbox
- elementHandle.click(): Click vào link hay button
Chừng đó là đủ cho chúng ta bắt đầu viết rồi.
Các bạn có thể xem danh sách toàn bộ API tại đây nhé: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md
Bắt đầu viết code thôi nào
Chúng ta sẽ dùng pupeeteer để test ngay lazada.vn cho nó nóng.
Đầu tiên, ta tạo một file mới tên là lazada.test.js.
Chúng ta bắt đầu set up đoạn code mở trình duyệt khi chạy unit test, đóng trình duyệt sau khi chạy xong. Các bạn nên để headless: false để dễ theo dõi trình duyệt nhé.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let puppeteer = require('puppeteer'); | |
let browser = null; | |
let page = null; | |
describe('Lazada test', () => { | |
// Code này được chạy khi bắt đầu chạy unit test | |
beforeAll(async() => { | |
browser = await puppeteer.launch(); | |
page = await browser.newPage(); | |
await page.setViewport({ | |
width: 1280, | |
height: 720 | |
}); | |
// Mặc định, timeout của jest là 5s. | |
// Vì web load có thể lâu nên ta tăng lên thành 60s. | |
jest.setTimeout(60000); | |
}); | |
// Đóng trình duyệt sau khi đã chạy xong các test case | |
afterAll(async() => { | |
await browser.close(); | |
}); | |
// Trước khi chạy mỗi test case, vào trang chủ của lazada | |
beforeEach(async() => { | |
await page.goto('https://lazada.vn'); | |
}); | |
}) |
1. Test chức năng tìm đồ lót của Lazada.vn
Để test chức năng search, ta thực hiện các bước như sau:
- Tìm khung search
- Gõ sexy underwear, ấn enter
- Sau khi kiểm tra, ta thấy sản phẩm là div có attribute là
data-qa-locator=product-item
- Đếm số lượng sản phẩm, nếu đủ 40 là ok.

Viết code thôi nào
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test('Search sexy underwear', async() => { | |
expect.assertions(1); | |
// Tìm khung search, gõ sexy underwear và bấm enter | |
const searchBox = await page.$('#q'); | |
await searchBox.type('sexy underwear'); | |
await searchBox.press('Enter'); | |
// Chờ trang load xong, tìm các phần tử item và đếm nếu đủ 40 | |
await page.waitForNavigation(); | |
const products = await page.$$('div[data-qa-locator=product-item]'); | |
expect(products.length).toBe(40); | |
}); |
2. Test nội dung hiển thị trên web tải app
Tương tự, ta cũng làm 2 bước:
- Click vào link “Tải App Lazada” góc trên bên trái.
- Kiểm tra nội dung hiển thị trong menu
Phần này mình dùng ví dụ xpath cho các bạn làm theo nha. Để lấy xpath của 1 element, ta mở Developer Tools lên, inspect element đó và Copy Xpath.

Tiện ghê chưa, khỏi cần viết CSS selector gì luôn! XPath lấy element riêng lẻ rất hiệu quả nhưng muốn lấy 1 list thì khó hơn nhé.
Code cũng ngắn gọn thôi hihi
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test('Install app', async() => { | |
expect.assertions(1); | |
// Tìm và click vào link | |
const appDownloadLink = await page.$x('//*[@id="topActionDownload"]'); | |
await appDownloadLink[0].click(); | |
await page.waitForNavigation(); | |
// Tìm element trên menu, lấy innerText của element đó | |
const breadCrumbHandle = await page.$x('/html/body/header/footer/div[2]/div[1]/ul/li[2]/span'); | |
const text = await page.evaluate(element => element.innerText, breadCrumbHandle[0]); | |
// Check nội dung element đó có chữ App Mobile tại Lazada | |
expect(text).toContain('App Mobile tại Lazada'); | |
}); |
Để kiểm tra kết quả, ta chỉ việc gõ npm run test thôi nhé.
Sau khi code đã chạy đúng, các bạn có thể bỏ headless:false đi để chạy test mà không cần mở browser nhé.
Bonus: Các bạn vào đây xem toàn bộ source code của bài viết nhe
https://github.com/conanak99/pupeteer-jest-test
Lợi ích của Automation Test
Như các bạn thấy đấy, việc viết script và manual test 2 chức năng trên chỉ mất khoảng 5 phút, nhưng viết automation test để chạy có thể mất 15-30 phút hoặc hơn.
Tuy vậy, khi code của lazada có gì thay đổi, ta chỉ việc mất 10-20 giây để test lại mà thôi.
Với những app phức tạp, việc test phải mất 2-3 ngày, mỗi lần sửa code hoặc thêm chức năng mới, không thể chạy test được mà phải chờ 1-2 tuần mới test 1 lần. Việc này dẫn đến chuyện ta rất dễ dàng bỏ sót bug hoặc tạo thêm bug mới.
Nếu đầu tư viết automation test ngay từ đầu, mỗi lần sửa code hoặc thêm chức năng mới, ta chỉ cần chạy lại automation test, chỉ mất 10-30s mà lại không tốn sức, dễ dàng tìm ra lỗi, đảm bảo hệ thống chạy tốt nữa.

Đấy, đó chính là lý do mà automation test mang lại rất nhiều value cho cả team và dự án; cũng là lý do mà automation QA engineer rất được săn đón.
Kết
Qua series này, các bạn đã học được cách sử dụng pupeteer, cũng như hiểu được tổng quát về ngành testing và automation test.
Các kiến thức về unit test, automation test trong series tuy không quá nhiều, nhưng cũng đủ để làm nền tảng cho bạn tự học và tự tìm hiểu.
Nếu có hứng thú với chủ đề này, bạn cứ để lại comment nha. Có nhiều người quan tâm thì mình sẽ tìm hiểu và viết thêm.
Lời khuyên cuối cùng của mình là: Nếu thực sự muốn theo đuổi nghiệp automation test thì các bạn nên tìm hiểu thêm về Selenium và một số testing framework khác như Appium, QTP nhé!

Hiện tại page.xpath hình như ko còn dùng nữa, thay = page.$x
Tiếp theo ở action await appDownloadLink.click(); sẽ báo lỗi “TypeError: appDownloadLink.click is not a function”. Em đã thử thay = page.click(appDownloadLink) nhưng vẫn lỗi.
A có thể check giùm e ko ạ?
LikeLike
Ừm để tối anh check lại nhé, hình như $x trả về 1 array chứ ko phải 1 item nên phải loop mới được
LikeLike
Anh vừa update lại bài viết cho đúng syntax của puppeteer v1.0.0. Cảm ơn e đã góp ý nhe :3
LikeLike
Cảm ơn a, e sẽ thử lại 😀
LikeLike
Cho mình hỏi, mình dùng puppeteer để cào dữ liệu. Tuy nhiên khi run thì chạy đc 1 lúc, khi web hiện notification lên, xong nó dừng luôn. Mình thử chạy lại, xong nhanh tay vào trong setting của chrome để tắt notification (chrome mỗi lần chạy là nó reset setting lại hết) mà cũng không đc, nó tự đứng lại luôn 😦
LikeLike
A cho e hỏi e dùng selenium để điều khiển chrome vào một nick instagram . Nó sẽ phải lần lượt vào trang chủ đăng bài , tìm kiếm , thả tim theo id nhất định , nhấn theo dõi … Giờ e muốn gửi ngầm một yêu cầu thả tim không cần phải điều khiển chrome vào id đó để để làm công việc như bình thường , bằng chính nick đó , trên đúng trình duyệt đó thì có được không a. A có thể gợi ý cho e được không. .
LikeLike
Quá hay luôn a ơi !
LikeLike
Hi anh,
Có thể dùng puppeteer mở shopee lên rồi copy các response API của nó trên tab network được không anh?
LikeLike