Rayrun
โ† Back to Discord Forum

Prevent Certain tests from running at the same time when using multiple workers (Locks / mutex)

Hi there, I have a suite of tests that are largely independent of each other, so I use the worker parallelism.

Unfortunately, a small subset needs access to on-disk files for upload tests. When these tests run at the same time, they end up interfering with each other and causing failures.

Does Playwright have a mutex/lock object that can block access to a resource on a worker level until they are released?

This thread is trying to answer question "Does Playwright have a mutex/lock object that can block access to a resource on a worker level until they are released?"

6 replies
garland2953

https://playwright.dev/docs/api/class-test#test-step there are discouraged methods in these docs for having tests run in parallel conditionally

garland2953

Or another solution would be to have duplicates of the file stored in different places, and each test accesses their own copy

Run a simple server to manage such test. I haven't done this before, but a server (not handling request in parallel) can solve your problem. Server can hold the lock/mutex in its own process, and you can block your tests by polling the server from worker process. Setting timeouts for such test cases would be the next problem. ๐Ÿ™‚

deadbeef01

You need to serialise that test or run that specific suite with 1 worker independently

We can create a websocket server to manage the global state, and lock/unlock the state in the test. For easy use, I have integrated it into a custom reporter.

import { test } from '@playwright/test';
import { useState } from 'monocart-reporter';

test('example test 1', async ({ browserName }) => {

    const state = useState({
        timeout: 10 * 1000
    });

    // lock
    await state.send('lock');

    console.log(`[${browserName}][test 1] get list`);
    const list = await state.get('list');
    console.log(`[${browserName}][test 1] receive list`, list);

    list.push(`${browserName} 1`);

    console.log(`[${browserName}][test 1] set list`, list);
    await state.set('list', list);

    // unlock
    await state.send('unlock');
});
// playwright.config.js
import { devices } from '@playwright/test';

let locking = false;
const waitingList = [];
const lockHandler = () => {
    if (locking) {
        return new Promise((resolve) => {
            waitingList.push(resolve);
        });
    }
    locking = true;
};
const unlockHandler = () => {
    if (waitingList.length) {
        const next = waitingList.shift();
        next();
        locking = true;
    } else {
        locking = false;
    }
};

export default {
    projects: [
        {
            name: 'chromium',
            use: devices['Desktop Chrome']
        },
        {
            name: 'firefox',
            use: devices['Desktop Firefox']
        }
    ],
    reporter: [
        ['dot'],
        ['monocart-reporter', {
            state: {
                data: {
                    list: [1, 2, 3]
                },
                onReceive: (action) => {
                    if (action === 'lock') {
                        return lockHandler();
                    }
                    return unlockHandler();
                }
            }
        }]
    ]
};

example: https://github.com/cenfun/monocart-reporter-test/tree/main/tests/global-state

@cenfun Thank you for the code snippets, I'll try it out and report back

Related Discord Threads

TwitterGitHubLinkedIn
AboutQuestionsDiscord ForumBrowser ExtensionTagsQA Jobs

Rayrun is a community for QA engineers. I am constantly looking for new ways to add value to people learning Playwright and other browser automation frameworks. If you have feedback, email luc@ray.run.