最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to share state between Playwright tests? - Stack Overflow

programmeradmin0浏览0评论

I'm learning Playwright and JavaScript concurrently so this may be an elementary question - I'm wondering how people would recommend sharing state - variable customerId in this case - between tests.

Example:

test.describe.only('Generate a new customer', () => {
  let customerId
  let baseUrl = process.env.SHOP_URL
  
  test('Create new customer', async ({ request }) => {
    const response = await request.post(baseUrl +    `/shopify/v5/customer`, {})
    
    const responseBody = JSON.parse(await response.text())
    expect(response.status()).toBe(200)
    customerId = responseBody.customerId //need to persist customerId to pass into following test

  })

  test('Update customer details', async ({ request }) => {
     const response = await request.post(baseUrl +    `/shopify/v5/customer/update`, {})
      {
        data: {
          customerId: customerId, //customerId is undefined here
          name: "Fred"
        },
      }
    )
    expect(response.status()).toBe(200)
  })

the customerId is clearly out of scope in the second test. I will probably refactor these to use a library such as Axios eventually because I am using the Playwright tests to generate data - I'm not actually testing the api here. In the meantime I just need customerId to be persisted in subsequent api calls.

I'm learning Playwright and JavaScript concurrently so this may be an elementary question - I'm wondering how people would recommend sharing state - variable customerId in this case - between tests.

Example:

test.describe.only('Generate a new customer', () => {
  let customerId
  let baseUrl = process.env.SHOP_URL
  
  test('Create new customer', async ({ request }) => {
    const response = await request.post(baseUrl +    `/shopify/v5/customer`, {})
    
    const responseBody = JSON.parse(await response.text())
    expect(response.status()).toBe(200)
    customerId = responseBody.customerId //need to persist customerId to pass into following test

  })

  test('Update customer details', async ({ request }) => {
     const response = await request.post(baseUrl +    `/shopify/v5/customer/update`, {})
      {
        data: {
          customerId: customerId, //customerId is undefined here
          name: "Fred"
        },
      }
    )
    expect(response.status()).toBe(200)
  })

the customerId is clearly out of scope in the second test. I will probably refactor these to use a library such as Axios eventually because I am using the Playwright tests to generate data - I'm not actually testing the api here. In the meantime I just need customerId to be persisted in subsequent api calls.

Share Improve this question edited Feb 3 at 9:04 mwopitz 5243 silver badges15 bronze badges asked Jan 6, 2022 at 11:12 SteerpikeSteerpike 1,84310 gold badges40 silver badges83 bronze badges 1
  • 1 stackoverflow.com/a/70614296/21881142 This will not work if you have a failed test in between. All data will be lost. – Vladimir Tanov Commented May 11, 2023 at 13:02
Add a comment  | 

4 Answers 4

Reset to default 9

To make your example work you need to run the tests in serial mode, something like this will work:

test.describe.serial('Generate a new customer', () => {
  let customerId
  let baseUrl = process.env.SHOP_URL
  
  test('Create new customer', async ({ request }) => {
    const response = await request.post(baseUrl +    `/shopify/v5/customer`, {})
    
    const responseBody = JSON.parse(await response.text())
    expect(response.status()).toBe(200)
    customerId = responseBody.customerId //need to persist customerId to pass into following test

  })

  test('Update customer details', async ({ request }) => {
     const response = await request.post(baseUrl +    `/shopify/v5/customer/update`, {})
      {
        data: {
          customerId: customerId, //customerId is undefined here
          name: "Fred"
        },
      }
    )
    expect(response.status()).toBe(200)
  })
});

That is anti-pattern, tests should be independent especially in playwright where tests run in parallel by default:

https://playwright.dev/docs/test-parallel

You can merge those two tests into one test.

If You still want to go that way I guess You can use fixtures or hooks to make it work, here are examples:

https://playwright.dev/docs/test-fixtures#without-fixtures

It's rarely true that you only need one test for specific action. For your example, it's definitely gonna be more than one test for create and update operations by the time when you set of tests would grow large.

Consider that you have several test for customer creation. It's better to have them grouped together in a one suite or in a related set of test suites. The same goes for the update operation.

As for sharing data between tests, you need to have all the shared data prepared in the beforeAll / beforeEach or in fixtures. At first build Test API, with basic operations.


class CustomerAPI {
    async createCustomer(){
        const response = await request.post(baseUrl + `/shopify/v5/customer`, {});

        const responseBody = JSON.parse(await response.text());
        expect(response.status()).toBe(200);
        return responseBody;
    }

    async updateCustomer(){
        const response = await request.post(baseUrl + `/shopify/v5/customer/update`,
        {
            data: {
                customerId: customerId, //customerId is undefined here
                    name: "Fred"
            },
        });
        expect(response.status()).toBe(200);
        return response;
    }
}

Of course it's very basic implementation example lacking any actual implementation details.

Then in your tests you can utilize this API, e.g. by using fixtures

type TestFixtures = {
    customerAPI: CustomerAPI;
};


// Extend base test by providing "todoPage" and "settingsPage".
// This new "test" can be used in multiple test files, and each of them will get the fixtures.
export const test = base.extend<TestFixtures>({
    customerAPI: async ({ page }, use) => {
        // Set up the fixture.
        const customerAPI = new CustomerAPI(page);
        await customerAPI.createCustomer();

        // Use the fixture value in the test.
        await use(customerAPI);

        // Clean up the fixture.
        await customerAPI.deleteCustomer();
    }
});
export { expect } from '@playwright/test';

You can make CustomerAPI stateful and store info about created customers.

If you don't like fixtures approach, you can go with beforeAll / afterAll route - use the same CustomerAPI in beforeAll, create all the needed data and then clean it in afterAll.

Use custom fixture to share state:

Instead of relying on global variables or workarounds, we can create a custom fixture to share the state between tests. This approach is more clean,maintainable, reusable therefore adheres to best practices.

  const { test } = require('@playwright/test');

// Step 1: Define a custom fixture for shared state
const sharedStateFixture = test.extend({
  sharedState: async ({}, use) => {
    const state = {}; // Object to hold shared state
    await use(state); // Pass the state to the tests
  },
});

// Step 2: Use the custom fixture in your tests
sharedStateFixture.describe('Tests with shared state', () => {
  sharedStateFixture('Test 1: Set shared state', async ({ sharedState }) => {
    sharedState.user = { name: 'John Doe', age: 30 }; // Set shared state
    console.log('Test 1: Shared state set', sharedState);
  });

  sharedStateFixture('Test 2: Use shared state', async ({ sharedState }) => {
    console.log('Test 2: Shared state used', sharedState);
    console.log('User name:', sharedState.user.name); // Access shared state
  });

  sharedStateFixture('Test 3: Modify shared state', async ({ sharedState }) => {
    sharedState.user.age = 31; // Modify shared state
    console.log('Test 3: Shared state modified', sharedState);
  });
});
发布评论

评论列表(0)

  1. 暂无评论