1
votes

Summary of problem: I'm writing several test suites (using Jest and Puppeteer) to automate tests for my AngularJS app's index.html page. Unfortunately, I'm seeing some odd behavior when I run my tests, which I think is related to the order in which Jest runs my various test suites.

Background: I'm using Jest (v24.8.0) as my testing framework. I'm using Puppeteer (v1.19.0) to spin up and control a Chromium browser on which to perform my tests.

My Code:

<!-- 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();
})();

Test Behavior: Sometimes when I run npm test it seems that my two test suites, 'form1' and 'form2' (which I defined with describe), are being run in parallel (although I know that's not possible in Javascript, so I am assuming Jest runs different test suites asynchronously). Either way, when I run my tests in non-headless mode, I can see that form1's name input is populated with 'jackie', even though it should be 'casey'. After that happens, form2 is never filled out (even though my second test suite is supposed to do just that) and the tests complete, after which Jest informs me that 'populate form 2' has failed. Again, this doesn't happen every time I run my tests, so you may not be able to replicate my problem.

My Questions:

  1. Does Jest run test suites in parallel/asynchronously? Note: I'm not talking about individual test's defined with test within the test suites, I know that those are run asynchronously if I pass them an async function.

  2. If Jest does run test suites asynchronously, how do I disable that? Or better yet, is smart/conventional/optimal to give different test suites different browser instances so that they are run in completely separate windows? Are there any other methods for ensuring test suites are run separately and/or synchronously?

  3. If Jest does not run test suites asynchronously, then why do you think I'm seeing this behavior?

I'm asking because I'd like to find a way to ensure that all my tests pass all of the time, instead of just some of the time. This will make it easier in the long run to determine whether or not my changes during development have broken anything.

Thanks in advance to all you Jest/Puppeteer hackers out there!

1

1 Answers

1
votes

By default Jest runs tests in each file concurrently depending on max workers, but runs all describe and tests blocks serially within a file.

If you want all all files run serially use run in band, this removes the workers pool.

However, i recommend refactor, you can nest describe blocks

// 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();
})

Update

In NodeJS you can spawn processes , in the case of jest, each process is a node instance and they communicate through standard IO. You can tell jest how many to spawn with the CLI options, however, i've encounter performance degradation when using slightly many workers with jest, due the fact that NodeJS instances are heavyweight, and spawn/sync might be more costly than actually just running in band or few workers.

Also, there is no garante on how they run, if you want to spawn 10 processes, your OS might schedule all to the same CPU thread, and they would be running in series.