banner
DIYgod

Hi, DIYgod

写代码是热爱,写到世界充满爱!
github
twitter
follow
bilibili
telegram
email
steam
playstation
nintendo switch

HeadlessChrome 自動化測試探索

埋點一直是 B 站 HTML5 播放器開發和測試過程中的一個痛點,埋點的種類和介面參數很多,測試很麻煩也很容易出錯。

雖然測試很麻煩,但它們的規則都很簡單,比如點擊或 hover 一個按鈕、錯誤上報、播放和性能上報,那麼能不能通過自動化的 E2E 測試來代替這些又繁瑣又機械化又容易出錯的測試工作呢?

在一次埋點線上事故後,我花了一天時間做了一些探索,最後效果還不錯,在這裡做一下簡單的總結。

編寫測試腳本#

模擬用戶操作就需要用到無頭瀏覽器,我採用了 Jest + Puppeteer 的組合。

Jest 是一個測試框架,Puppeteer 是用來控制 Chrome 或 Chromium。

選擇 Jest 是因為我對 Jest 最熟悉,然後又找到了一個 preset: jest-puppeteer,不是必需的,但它可以簡化很多 Puppeteer 操作。

安裝依賴:

npm install --save-dev jest jest-puppeteer puppeteer

測試腳本很簡單:

describe('log', () => {
    beforeAll(async () => {
        page.goto('https://www.bilibili.com/video/av44890855');
    });

    it('play_screen', async (done) => {
        page.on('request', (request) => {
            if (request.url().match(/^https:\/\/data\.bilibili\.com\/log\/web\?play_screen...參數參數/)) {
                done();
            }
        });
        page.click('video');
    });
});

讓 HeadlessChrome 打開一個播放頁,監控頁面請求的介面,模擬點擊 video 元素,監控到瀏覽器請求了 play_screen 埋點即測試成功。

看起來沒什麼問題,開開心心地執行了測試,結果 failed。

發生了什麼?配置 headless: false 觀看了一下測試過程。

headlesschrome1

發現是因為檢測到瀏覽器不支持 HTML5 播放器,加載了 Flash 播放器。

Puppeteer 文檔裡說道

Puppeteer is bundled with Chromium--not Chrome...Puppeteer does not support licensed formats such as AAC or H.264。

解決方法也很簡單,把 Puppeteer 自帶的 Chromium 換成本地的 Chrome。

launch: {
    executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
}

測試通過。

headlesschrome2

Chrome as a service#

剛才使用了本地的 Chrome,會依賴本地環境,而且想作為自動化測試跑在測試機上也是不行的。

所以我又在測試機上跑了一個 docker 容器:browserless/chrome,它可以把 Chrome 當做一個 service,測試腳本使用 websocket 協議操作 docker 里的 Chrome,這樣就避免了依賴本地 Chrome。

啟動容器:

docker pull browserless/chrome:release-chrome-stable
docker run -d -p 3000:3000 browserless/chrome:release-chrome-stable

使用:

connect: {
    browserWSEndpoint: 'ws://localhost:3000'
}

劫持 js#

這樣用的是線上版本,根本沒有測試本地代碼啊!

哦,忘了說了,還需要把線上 js 劫持為本地版本。

await page.setRequestInterception(true);

page.goto('https://www.bilibili.com/video/av44890855');
page.on('request', (request) => {
    if (request.url().match(/player\.js/)) {
        request.respond({
            status: 200,
            contentType: 'application/javascript',
            body: fs.readFileSync('dist/release/player.js').toString()
        });
    } else {
        request.continue();
    }
});
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。