I have just completed my first two playwright tests, but I am not sure how to optimize them.

The scenario is the following:

  1. I log in to the system with an administrative user.
  2. I create a new user and save the generated username and password using the following code:
// copy username and password
const username = await page.locator('#username0').innerText();
const password = await page.locator('#passwd0').innerText();
  1. I log out the admin user.
  2. I log in the created user (using the const created above), accept the terms and conditions and answer 220 questions, saving a screenshot of every 20th question.
for (let x = 1; x <= 220; x++) {
    if (x % 20 === 0) {
        await storeScreenshot(popupPage, testInfo, 'test Q' + x);
    await popupPage.waitForLoadState();
    await popupPage.locator(user.selector).click();
    if (x % 20 === 0) {
        await storeScreenshot(popupPage, testInfo, 'test Q' + x + ' - selected');
    await popupPage.locator('#submitknap').click();

I am very happy that this works, and it is even parameterized with a csv for testing different answer patterns.

If any step in this fails, the whole case fails.

Ideally the admin part is not necessary as a part of the test and is reusable, but creating a user IS something I want to have. I could create some static users and wipe them before running each test, but I like the dynamic approach of creating a user prior to logging it in. I also have some environments that are not easily reset.

I am in doubt as to how I can split this test scenario up. Should I have the admin part as a helper function called by the testcase, or as a smaller testcase prior to calling the main testcase?


For a little perspective this test takes 3 minutes to complete. a complete run of 6 parameters in three browsers takes 16 minutes.

1 Answer 1


Should I have the admin part as a helper function called by the test case, or as a smaller test case prior to calling the main test case?

Yes, have the admin > create a new user part as a helper function. This code can then be reused for other tests in the future.

Yes, you can call this from the main test case. Or, the better option is to add it to the beforeAll() method as part of the setup for the test since it is just a setup for the test.

Will that optimize the test to make it run faster? Not really. This will help in readability, reusability of code, make your test framework extendable for the future. Code once, reuse many times!

accept the terms and conditions and answer 220 questions, saving a screenshot of every 20th question.

Well, you don't really need to take a screenshot on every 20 questions. You're creating 11 image files for each successful test run. You should take a screenshot on test failure. That can save some time.

It seems likely that the lengthy part of the test is answering these 220 questions!

Why are there 220 questions a new user needs to answer? That's the context we are missing. Is there a way to skip these and answer them later (separate test case)? (It's smart you have these questions parameterized!)

Can the new user test really just stop after you can login successfully with that new user? This proves the new user was created successfully and can be used.

Other than those 220 questions, there's no other real way to make this run faster!

  • Good point about the screenshots - I will have to investigate how I go about doing that. I have separate tests for the questions - answering them via an API. That may be an Idea to "inject the answers" to the majority of the questions. I may have the wrong approach to testing "as a real user", hence my (potentially flawed-formulated) questions. My dilemma is that an overview of the questionnaire is displayed, which is calculated from the answers.
    – JoSSte
    Commented May 6 at 18:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.