【问题标题】:Does Jest run test suites defined with describe() asynchronously?Jest 是否异步运行用 describe() 定义的测试套件?
【发布时间】:2019-12-13 12:48:55
【问题描述】:

问题总结:我正在编写几个测试套件(使用 Jest 和 Puppeteer)来自动测试我的 AngularJS 应用程序的 index.html 页面。不幸的是,我在运行测试时看到了一些奇怪的行为,我认为这与 Jest 运行我的各种测试套件的顺序有关。

背景:我使用 Jest (v24.8.0) 作为我的测试框架。我正在使用 Puppeteer (v1.19.0) 来启动和控制一个 Chromium 浏览器来执行我的测试。

我的代码:

<!-- index.html -->
<div ng-app="myApp" ng-controller="myCtrl as ctrl">
    <form name="form1">
        <md-input-container>
            <label class="md-required">Name</label>
            <input ng-model="ctrl.name1"></input>
        </md-input-container>
        <md-dialog-actions>
            <button class="md-primary md-button" ng-transclude type="submit">Submit</button>
        </md-dialog-actions>
    </form>
    <form name="form2">
        <md-input-container>
            <label class="md-required">Name</label>
            <input ng-model="ctrl.name2"></input>
        </md-input-container>
        <md-dialog-actions>
            <button class="md-primary md-button" ng-transclude type="submit">Submit</button>
        </md-dialog-actions>
    </form>
</div> 
// index.spec.js
const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({headless: false});
    const page = await browser.newPage();
    await page.goto('https://my-site.com');

    describe('form 1', async () => {
        test('populate form 1', async () => {
            let formSelector = 'form[name="form1"]';
            await page.waitForSelector(formSelector+' input', {timeout: 3000});
            await page.click(formSelector+' input');
            await page.keyboard.type('casey');
            let submitButtonSelector = 'form[name="form1"] button[type="submit"]';
            await page.click(submitButtonSelector);
        });
    });
    describe('form 2', async () => {
        test('populate form 2', async () => {
            let formSelector = 'form[name="form2"]';
            await page.waitForSelector(formSelector+' input', {timeout: 3000});
            await page.click(formSelector+' input');
            await page.keyboard.type('jackie');
            let submitButtonSelector = 'form[name="form2"] button[type="submit"]';
            await page.click(submitButtonSelector);
        });
    });
    await browser.close();
})();

测试行为: 有时当我运行npm test 时,我的两个测试套件'form1''form2'(我用describe 定义)似乎正在并行运行(尽管我知道那不是可能在 Javascript 中,所以我假设 Jest 异步运行不同的测试套件)。无论哪种方式,当我在非无头模式下运行测试时,我可以看到form1 的名称输入填充了'jackie',即使它应该是'casey'。在那之后,form2 永远不会被填写(即使我的第二个测试套件应该这样做)并且测试完成,之后 Jest 告诉我'populate form 2' 失败了。同样,每次我运行测试时都不会发生这种情况,因此您可能无法复制我的问题。

我的问题:

  1. Jest 是否并行/异步运行测试套件? 注意:我不是在谈论在测试套件中使用test 定义的单个测试,我知道如果我向它们传递async 函数,它们会异步运行。

    李>
  2. 如果 Jest 确实异步运行测试套件,我该如何禁用它?或者更好的是,为不同的测试套件提供不同的browser 实例以便它们在完全独立的窗口中运行是智能/传统/最优吗?有没有其他方法可以确保测试套件单独和/或同步运行?

  3. 如果 Jest 不异步运行测试套件,那么您认为我为什么会看到这种行为?

我之所以这么问,是因为我想找到一种方法来确保我的所有测试始终通过,而不是仅在某些时候通过。从长远来看,这将更容易确定我在开发过程中的更改是否破坏了任何东西。

提前感谢所有 Jest/Puppeteer 黑客!

【问题讨论】:

    标签: javascript angularjs jestjs puppeteer


    【解决方案1】:

    默认情况下,Jest 根据max workers 在每个文件中同时运行测试,但runs all describe and tests blocks serially within a file

    如果您希望所有文件串行运行,请使用run in band,这将删除工作池。

    不过,我推荐重构,你可以嵌套describe

    // instead of IIFE, nest describes for better parsing for jest and readability
    describe('forms', async () => {
        const browser = await puppeteer.launch({headless: false});
    
        describe('form 1', () => {
            test('populate form 1', async () => {
                // make every test independent of the other to avoid leaky scenarios
                const page = await browser.newPage();
                await page.goto('https://my-site.com');
                let formSelector = 'form[name="form1"]';
                await page.waitForSelector(formSelector+' input', {timeout: 3000});
                await page.click(formSelector+' input');
                await page.keyboard.type('casey');
                let submitButtonSelector = 'form[name="form1"] button[type="submit"]';
                await page.click(submitButtonSelector);
            });
        });
        describe('form 2', () => {
            test('populate form 2', async () => {
                const page = await browser.newPage();
                await page.goto('https://my-site.com');
                let formSelector = 'form[name="form2"]';
                await page.waitForSelector(formSelector+' input', {timeout: 3000});
                await page.click(formSelector+' input');
                await page.keyboard.type('jackie');
                let submitButtonSelector = 'form[name="form2"] button[type="submit"]';
                await page.click(submitButtonSelector);
            });
        });
    
        await browser.close();
    })
    

    更新

    在 NodeJS 中你可以 spawn processes ,在开玩笑的情况下,每个进程都是一个节点实例,它们通过标准 IO 进行通信。您可以使用 CLI 选项告诉 jest 生成多少个,但是,由于 NodeJS 实例是重量级的,并且生成/同步可能比实际运行成本更高在乐队或少数工人。

    此外,它们的运行方式也没有任何保证,如果您想生成 10 个进程,您的操作系统可能会将所有进程调度到同一个 CPU 线程,并且它们将串联运行。

    【讨论】:

    • 感谢@Edgar José Rodríguez Garcia!很有意思。如果 Javascript 是单线程语言,Jest 怎么可能同时运行测试?
    • 感谢nodejs,您可以spawn processes,在开玩笑的情况下,每个进程都是一个节点实例,它们通过消息传递(标准IO)进行通信。您可以使用 CLI 选项告诉 jest 要生成多少个,但是,当使用 jest 使用少量工作人员时,我遇到了性能下降,因为 nodejs 实例是重量级的,并且 spawn/sync 可能比实际上更昂贵在乐队或少数工人中跑步。
    • 这不起作用: > 不支持从“describe”返回 Promise。测试必须同步定义。
    猜你喜欢
    • 2018-10-14
    • 2022-01-05
    • 2019-11-24
    • 2019-10-21
    • 2020-08-21
    • 2018-12-28
    • 2020-11-25
    • 2020-03-01
    • 2022-11-09
    相关资源
    最近更新 更多