Skip to content

[Feature] Locks to avoid concurrent runs of specific tests #21484

Closed

Description

@joerg1985

Errors due to the concurrent runs is a common issue in test automation.
Currently there is no support for locks to avoid this in playwright and the tests must be run sequential, as far as i can tell.
Usually only a small set of tests will require lock, but the test execution is significant slower sequentially.

It is also not possible to implement them outside playwirght for several reasons:

  • using a locking mechanism in the test / beforeEach is not possible due to the timeout of the test method
  • using a fixture with a custom timeout would be an option, but with differen locks i need different fixtures
    In both options the workers will be blocked until the lock is aquired and we might end in a deadlock if multiple locks are needed.

Playwirght could support locks on test level by adding options to the test(title, callback) method with a list of named locks.
The excution of the test will be block until no other test is currently using these locks. To avoid deadlocks the usage of locks could be limited to not allow nested locks.

test("call the external interface", (page) => { page... }, { locks: ["LegacySystem"]});

Activity

  1. cpolera commented on Mar 8, 2023

    @cpolera

    You could probably use the new TestProject.dependencies to have a project that runs the sequential tests before running the rest of the tests.

    see: https://github.com/microsoft/playwright/releases/tag/v1.31.0

    edit: actually they probably wont work since a single failure in the dependency will prevent running the second project.
    At that point I'd say its probably best to just run two separate projects and have them write to the same directory for the HTML report if thats a need.

  2. mxschmitt commented on Mar 8, 2023

    @mxschmitt
    Contributor

    Looks like a duplicate of #14084 to me.

  3. joerg1985 commented on Mar 8, 2023

    @joerg1985
    Author

    The other one is per test, this one is about more dynamic locks.

  4. mxschmitt commented on Mar 9, 2023

    @mxschmitt
    Contributor

    Could you let us know about your use-case? We usually recommend putting critical things in the same file, since we don't do any parallelise there (except fullyParallel is true).

  5. joerg1985 commented on Mar 15, 2023

    @joerg1985
    Author

    Max Schmitt (@mxschmitt) sure, here are our current and future use-cases as far i can tell for now.

    1. We need to increment numbers, earch number can only be used once.
      a) read the latest number
      b) fill the UI with the latest number + 1 and other infos, this takes about 40s
      c) submit the form -> only one test will pass

    2. We need to do UI operations calling external interfaces to other applications.
      If this interface is called concurrently it is locked, it is not desinged for concurrent uses.

    3. (future) Integrate hardware into the process and trigger actions on it. We can only trigger one by one to not disturb each others (e.g. due the reset of the hardware before starting the actual test), be able to assign the logs / generate data to a test run.

    We might workaround for 1. and 2. by using different users for earch worker (based on the worker index?), but they will use the same data. So we probably must use different data for each worker, but then we need to get the base data maintained in the external applications too. This approach won't scale if we need more workers in the future.

  6. yury-s commented on Mar 15, 2023

    @yury-s
    Member

    We might workaround for 1. and 2. by using different users for earch worker (based on the worker index?), but they will use the same data.

    Can you elaborate on what is wrong with using the same data for each of the test users running in parallel?

    If this interface is called concurrently it is locked, it is not desinged for concurrent uses.

    If the interface is not designed for concurrent use you'd better run your tests in serial mode (or have a unique user per parallel worker). Why is it not an option for you? As far as I understand, you want to serialize calls to the service rather than entire tests and this would be outside of playwright scope.

  7. joerg1985 commented on Mar 16, 2023

    @joerg1985
    Author

    I think i need to explain where the concurrency is comming from, this is done to reduce the testing time with different browsers. fullyParallel is false, workers is 3 and projects is 3 (one project for each browser).
    Each worker will process the tests serial, but the different browsers will all start at the same test, following the same order of tests. So we need a locking between the workers (in this case browsers) and not inside one worker.

    Can you elaborate on what is wrong with using the same data for each of the test users running in parallel?

    We cannot check the processing is correct for each test run, if the test runs will concurrently overwrite the results.

    If this interface is called concurrently it is locked, it is not desinged for concurrent uses.

    this is what is currently happening on the time line, the tests before the critical area are taking different amount of time, so we will not hit the test at the same time, but still while another browser is running:
    00:08:02 Chrome - will upload data
    00:09:10 Firefox- will upload data
    00:08:03 Chrome - data is processing
    00:09:13 Firefox - upload failed / due to Chrome data is processing
    00:11:00 Chrome - data is processed
    00:11:01 Chrome - check the data

    Serializing the single calls to the service will not ensure the "check the data" step will pass, a different browser might have overwritten the data in the mean time, so we would need to lock until the complete test is done.

  8. 1 remaining item

  9. dacloutier-logmein commented on Apr 23, 2024

    @dacloutier-logmein

    Adding another use case here.

    I'm testing a Call Center webRTC application, in which some actions needs to be evaluated before / during / after a call is placed. Of course users can't be active on 2 calls at the same (caller and callee and supervisor).

    Creating 1 set of users per test isn't viable either ( would be 3 users per test X number of context [local, ci, regression, healthcheck, smoketests] X environments [local, dev, staging, nightly, live] X each browsers we need to support [chrome, ff, webkit]

    Most tests that do not involve placing calls could be fully parallel and it wouldn't be an issue, however, anything that has to place a call cannot be executed in that fashion.

    As mentioned above, using dependencies wouldn't really help as a single failure would prevent the subsequent project to run.

  10. mcengel commented on Dec 3, 2024

    @mcengel

    Adding Yet Another Use Case

    I am currently testing a document management system that is structured into multiple modules. We designed our test structure to mirror the organization of the modules in our project, ensuring that each functionality has its own dedicated test file. However, we are facing a challenge: our modules sometimes modify or utilize the global settings of the system, leading to interference between tests.

    Our current solution is to consolidate all these tests into a single test file. This approach has several drawbacks, including the need to increase our global timeout due to the sheer number of tests, even when using test.slow().

    Another example involves our Statistics Module. In our system, users can track statistics such as the number of logins or document uploads over a specific time period. The page for activating tracking and the page for viewing the tracked data are two distinct modules, yet they must reside in the same test file.

    While it is possible to refactor our teststructure so that all interfering tests are grouped together, this would not only be confusing and unintuitive but also inefficient in many cases. For instance, consider three tests: A, B, and C. Tests A and B interfere with each other, as do tests B and C. Without a locking mechanism, I would be forced to place all three tests in one test file, even though tests A and C could run in parallel without any issues.

  11. patrickmullen-b2d commented on Aug 20, 2025

    @patrickmullen-b2d

    I have a single test that uses some canned data, modifies that data, and then resets the data. I want this test to run in multiple browsers, and have most of the tests in the suite run in parallel. But if a firefox worker and a chrome worker both run this test at the same time, the state can get mangled.

    Options to solve this:

    • Create one dataset for each project: more data to track
    • Separate the test runners to run this test in each project sequentially, and other runs in parallel: more complicated configuration to maintain
    • Run all tests sequentially: slow
    • Put each browser case for this test in a single file so that file is run sequentially: odd special case, and some code reuse. (Probably how I'm going to handle it without this locking mechanism)

    A simpler method would be if a test case could be given a lock, and the playwright test worker would skip any tests that are currently locked.

  12. patrickmullen-b2d commented on Aug 20, 2025

    @patrickmullen-b2d

    It's not that bad. Here's my tsconfig:

        projects: [
            {
                name: 'chromium',
                grepInvert: /firefox|webkit/,
                use: {...devices['Desktop Chrome']},
            },
    
            {
                name: 'firefox',
                grepInvert: /chromium|webkit/,
                use: {...devices['Desktop Firefox']},
            },
    
            {
                name: 'webkit',
                grepInvert: /firefox|chromium/,
                use: {...devices['Desktop Safari']},
            },
        ],
    

    Then in my test file:

    test(
        'test1 -chromium',
        {tag: '@chromium'},
        actual_test_function
    );
    
    test(
        'test1 -webkit',
        {tag: '@webkit'},
        actual_test_function
    );
    
    test(
        'test1 -firefox',
        {tag: '@firefox'},
        actual_test_function
    );
    

    In the rare case I have tests I only want to run in a specific browser I've already set up a mechanism for it so that's a plus. I'm not sure if this helps with other use cases for a lock.

  13. alexchexes commented on Feb 20, 2026

    @alexchexes
    Contributor

    Any chance this will be considered to be implemented?

  14. dacloutier-logmein commented on Feb 23, 2026

    @dacloutier-logmein

    On my end I gave up waiting, ended up creating a resource lock service, tests requests a lockable resource, service either accept of denies the lock according to availability.

    Computing the resource to request is the test's responsibility, service is as "dumb" as it can be.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions