Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Get access to fixtures in test.describe block #7482

Closed
Daemos87 opened this issue Jul 6, 2021 · 6 comments
Closed

[Feature] Get access to fixtures in test.describe block #7482

Daemos87 opened this issue Jul 6, 2021 · 6 comments
Labels
feature-test-runner Playwright test specific issues

Comments

@Daemos87
Copy link

Daemos87 commented Jul 6, 2021

I was trying to generate some tests for each value of an array, and while the following code is valid

import test, { expect } from "@playwright/test";

test.describe('Test group',()=>{

    const something = [1,2,3,4]

    something.forEach(x => {
        test(`testing for ${x}`,async ()=>{
            expect(x).toBeGreaterThan(1)
        })
    })
})

The following does not compile

// something in this example should be  a fixture with type "number[]"
test.describe('Test group',({something})=>{

    something.forEach(x => {
        test(`testing for ${x}`,async ()=>{
            expect(x).toBeGreaterThan(1)
        })
    })
})

The issue here is that describe's second parameter's type is "()=>void", so the arrow function does not accept any fixture, and so, correct me if i'm wrong, it looks like there's no way to generate tests from some fixture.

Is there any plan to support my use case in the future ?

thanks in advance for the great work!

@pavelfeldman pavelfeldman added feature-test-runner Playwright test specific issues triaging labels Jul 7, 2021
@pavelfeldman
Copy link
Member

@dgozman ^^

@dgozman
Copy link
Contributor

dgozman commented Jul 7, 2021

@Daemos87 This would be a hard thing to make work.

Could you please give some more details? For example, why something is a fixture? Perhaps you could require the list instead of making it a fixture, similar to your first example? This way you can share the code, but do not have to bother with a fixture.

const something = require('./my-helper-something');

test.describe('Test group', () =>{
    something.forEach(x => {
        test(`testing for ${x}`,async ()=>{
            expect(x).toBeGreaterThan(1)
        })
    })
})

@Daemos87
Copy link
Author

Daemos87 commented Jul 7, 2021

@dgozman thanks for looking into this

maybe it's just a matter of personal taste, but i think that writing something like this

// fixture.ts
import { test as base } from "@playwright/test";
import { startDBAndReturnUrl, stopDB, doAQuery } from "./utils";
interface DB {
  ip: string;
  queryResult: string[];
}

const test = base.extend<{}, DB>({
  ip: [
    async ({}, use) => {
      const ip = await startDBAndReturnUrl();
      await use(ip);
      await stopDB();
    },
    { scope: "worker" },
  ],
  queryResult: [
    async ({ ip }, use) => {
      await use(await doAQuery(ip));
    },
    { scope: "worker" },
  ],
});

export default test;
//some.test.ts
import test from "./fixture";

test.describe("Test that need a database", ({ queryResult }) => {
  queryResult.forEach((element) => {
    test(`Testing ${element}`, async () => {
      // do something with element
    });
  });
});


});

is a lot nicer and idiomatic than writing something like this

import test from '@playwright/test'
import {doAQuery,stopDB,startDBAndReturnUrl} from './utils'


test.describe('Tests that need a database',async ()=>{
    
    let ip: string

    test.beforeAll(async ()=>{
       ip =  await startDBAndReturnUrl()
    })
    
    test.afterAll(async ()=>{
        await stopDB()
    })

    const values = await doAQuery(ip)

    values.forEach(async (element)=>{
        test(`testing ${element}`,async ()=>{
            // do something with element
        })
    })

})

And here's the utils.ts file

export async function startDBAndReturnUrl() {
    console.log('starting a db')
    const randomIp = `127.0.0.1:${Math.floor(Math.random() * 9999)}`
    console.log(`db listening at ${randomIp}`)
    return `127.0.0.1:${randomIp}`
}


export async function stopDB() {
    console.log('stoppin a db')
    
    console.log(`db stopped`)
}


export async function doAQuery(ip:string){
    console.log(`Querying dtabase at ${ip}`)
    return ['a','query','result']
}

Maybe this example is an extreme case, but it's the first thing that comes to my mind and that can be simplified with a describe level fixture.

Let me know if that makes sense and thanks for your time.

@dgozman
Copy link
Contributor

dgozman commented Jul 7, 2021

@Daemos87 Thank you for the example, it makes sense.

Unfortunately, this is not supported and I do not see an easy way to support it. Two available workarounds for you:

  1. Instead of using test.describe and multiple test blocks, make a single test that uses the fixture. You can check each element in the for loop. We are looking into adding test.step API so that you can have multiple steps in a single test - in your case that would be one step per element.

  2. Put your database code into global setup file to execute it once. You can do whatever you want in the global setup phase. In this particular case, I'd store the data in some file or env variable, and then create multiple tests by reading from the file/env in your describe block.

@Daemos87
Copy link
Author

Daemos87 commented Jul 7, 2021

@dgozman

No worries, and thanks for the suggestions.

We are looking into adding test.step API so that you can have multiple steps in a single test - in your case that would be one step per element.

This is actually a great news, and a step API is something that i think is missing in most of the other test runners out there.

Can i make a suggestion in this regard ? I think that a it could be useful for a user to decide which test's step is a optional and which not.

something like this :

// if this step fail the subsequent test's steps are skipped and the test is marked as failed
// 'cause the test cannot go on without a login
test.step({description : 'Login', optional: false} ,()=>{
  doLogin()
})

//if this step fail the test is marked as failed but the subsequent test's steps are executed,
// 'cause while the expectation is failed i can go on with the test 

test.step({description : 'Assertions', optional: true} ,()=>{
  expect(true).toBe(true)
  expect(true).toBe(false)
})

@klhex
Copy link
Contributor

klhex commented Sep 7, 2021

@Daemos87 If you're still interested in the feature that you suggested in your last comment here please upvote #7786.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-test-runner Playwright test specific issues
Projects
None yet
Development

No branches or pull requests

4 participants