vitalets / playwright-bdd

BDD testing with Playwright runner
https://vitalets.github.io/playwright-bdd/
MIT License
318 stars 40 forks source link

Question: Beforeall & Afterall Not Working Properly #216

Closed satyabrat105 closed 1 month ago

satyabrat105 commented 3 months ago

In my project , i have one featurefile, 7 scenarios. in hooks i am using beforeall,before,after,afterall. when i executed the featurefile, before every scenario it is executing beforeall and after complete the scenario it executed afterall. 7times it executed beforeall and afterall. am i doing wrong? plaese share your feedback .

vitalets commented 3 months ago

Please, try to reproduce the bug on https://github.com/vitalets/playwright-bdd-example and share the link here. Also note that BeforeAll / AfterAll are called in each worker.

farnsword-ok commented 2 months ago

Also note that BeforeAll / AfterAll are called in each worker How can I ensure the whole featurefile is executed on the same worker? Basically, I need to have a precondition for the featurefile, so it executes only once for this particular featurefile, but not for any other featurefile.

farnsword-ok commented 2 months ago

Thanks for the answer @maxtuzz. I've tried Background, and according to cucumber documentation and the behavior I was getting from using it - Background is executed before each test, not even once per worker, but strictly before each test. It seems the purpose of background is to avoid duplication in the first steps of scenarios.

vitalets commented 2 months ago

How can I ensure the whole featurefile is executed on the same worker?

By default Playwright executes the whole file inside single worker, until you set fullyParallel option in the Playwright config.

The solution with Background from @maxtuzz in general works (the comment is removed now). The downside - it requires additional code to ensure the background is executed only once (setting some global flag). For example:

Feature: Some feature

    Background: 
      Given ensure user exists in database

    Scenario: ...

Example of step implementation:

let executed = false;
Given('ensure user exists in database', async ({ page }) => {
  if (!executed) {
     executed = true;
     // create user in database
  }
});

Here user will be created only once before feature file.

Note: Although it works, I'm not fully satisfied with the approach. Global variables in hooks are not honored by Playwright team as well. The best approach is to migrate to Playwright fixtures. Or, if keep using hooks - it's better to explicitly mark code as executed before each feature, like we do for other hooks. I've made some research and created a proposal in #219.

farnsword-ok commented 2 months ago

That's what I'm trying to implement right now. The main issue here is that even global variables in the file, where I describe Background stepdefinitions are valid only within a single worker, therefore, these variables should be made even more global, and as an attempt I'm trying to use a file where I will track for which feature which precondition was executed. Even though I don't have parallel execution enabled the worker changes whenever the test fails (apparently, to ensure clean env for the next test start). Thanks for the suggestion about the fixtures, I will look into them to see if they fit the purpose.

satyabrat105 commented 2 months ago

import { createBdd,test} from 'playwright-bdd'; import {Browser,BrowserContext,Page} from 'playwright';

let browser:Browser; let context: BrowserContext;

const {BeforeAll,AfterAll,Before,After} = createBdd(test);

BeforeAll(async()=>{

browser= await invokeBrowser(); context= await browser.newContext();

});

AfterAll(async()=>{

await browser.close();

}); it's my hoook file, how can i use wroker in beforeall

farnsword-ok commented 2 months ago

Not sure how exactly you want to use worker, but you can access info about the current worker by declaring a hook like this: BeforeAll(async ({$workerInfo}) => { In the hook body, you can refer to this incoming parameter $workerInfo.

vitalets commented 2 months ago

Even though I don't have parallel execution enabled the worker changes whenever the test fails (apparently, to ensure clean env for the next test start)

Yeap, this is the approach Playwright follows - if something fails, perform the whole setup from scratch. So with global variable, you may have extra run of background setup in case of fails. Isn't it suitable?

satyabrat105 commented 2 months ago

Hi vitalets, in my current playwright-bdd framework,its based on test level scope .for that, every execution beforeall method is executing. so i want a worker level scope. can you please give a example where worker level scope is used in hooks or in page fixture.

farnsword-ok commented 2 months ago

Even though I don't have parallel execution enabled the worker changes whenever the test fails (apparently, to ensure clean env for the next test start)

Yeap, this is the approach Playwright follows - if something fails, perform the whole setup from scratch. So with global variable, you may have extra run of background setup in case of fails. Isn't it suitable?

Not really. Example: In one of my cases, I'm creating several documents, that will be used later for testing the filter feature. Therefore, even if one of the tests fails I don't need to create another set of documents, so this extra run of background setup is not really acceptable.

vitalets commented 2 months ago

In one of my cases, I'm creating several documents, that will be used later for testing the filter feature. Therefore, even if one of the tests fails I don't need to create another set of documents, so this extra run of background setup is not really acceptable.

Got it. With such external state, the only way I see - is to check inside the background step, are there documents created or not. If it's API call, you can create some lock file in the tmp dir after successful documents creation.

Even after #219 beforeFeature hook will rerun if something fails, here we should follow the Playwright's architecture.

vitalets commented 2 months ago

Hi vitalets, in my current playwright-bdd framework,its based on test level scope .for that, every execution beforeall method is executing. so i want a worker level scope. can you please give a example where worker level scope is used in hooks or in page fixture.

const { BeforeAll, Before, Given } = createBdd();

BeforeAll(async function ({ $workerInfo, browser }) {
  console.log("BeforeAll", $workerInfo.workerIndex, browser.browserType());
});

Before(async function ({ $testInfo, browser }) {
  console.log("Before", $testInfo.workerIndex, browser.browserType());
});

Given('I open page', async ({ page, $testInfo, browser }) => {
  console.log("Step", $testInfo.workerIndex, browser.browserType());
  await page.goto(url);
});
farnsword-ok commented 2 months ago

In one of my cases, I'm creating several documents, that will be used later for testing the filter feature. Therefore, even if one of the tests fails I don't need to create another set of documents, so this extra run of background setup is not really acceptable.

Got it. With such external state, the only way I see - is to check inside the background step, are there documents created or not. If it's API call, you can create some lock file in the tmp dir after successful documents creation.

Even after #219 beforeFeature hook will rerun if something fails, here we should follow the Playwright's architecture.

This particular example relates to the UI tests, so checking the actual documents on UI will add some execution time. I'm trying the approach of having a properties file in a file system, where the background steps will write something like: Feature_1.Documents_Creation_Precondition=true when this precondition is executed

If we go parallel at some point (at the moment there are some limitations of the system under test that prevent us from doing so) will also have to introduce some synchronization to operations with this file and add some info about the thread or something similar to the entries in the file.

vitalets commented 2 months ago

so checking the actual documents on UI will add some execution time

You mean that you need to wait for documents to be loaded into browser UI? Shouldn't actual test steps (e.g. filter checks) wait for that anyway?

will also have to introduce some synchronization to operations with this file and add some info about the thread or something similar to the entries in the file

Or maybe even sqlite file to handle concurrency issues.

vitalets commented 2 months ago

In one of my cases, I'm creating several documents, that will be used later for testing the filter feature. Therefore, even if one of the tests fails I don't need to create another set of documents, so this extra run of background setup is not really acceptable.

I've made a deeper search in Playwright issues for similar topic. There are many people, who have exactly the same reasonable request - run beforeAll only once:

Lets track these issues, I hope Playwright will provide and option for that.

satyabrat105 commented 2 months ago

class MyPage{ constructor (public page:Page){} async openlink(name:string){ await this.page.getByRole('link',{name}).click(); } }

export cont test= base.extend<{},{pagefixture:MyPage}>({ pageFixture:[async({page},use)=>{ await use(new MyPage(page)); },{scope:'worker'}], });

Here i am trying use wroker scope then i am getting issue like property page doesnot exist. if i use testscope then there is no issue. but i required workerscope. how to do that in fixture ?

vitalets commented 2 months ago

Here i am trying use wroker scope then i am getting issue like property page doesnot exist. if i use testscope then there is no issue. but i required workerscope. how to do that in fixture ?

Page is not available in worker scoped fixture, because page is re-created for each test. Imagine worker with 3 tests - which page instance should be passed into worker fixture, that is called after worker start?

satyabrat105 commented 2 months ago

as per playwright-bdd document :

import { createBdd } from 'playwright-bdd';

const { BeforeAll, AfterAll } = createBdd();

BeforeAll(async function ({ $workerInfo, browser }) { // runs when each worker starts });

please give some suggestion what i need to implement in beforeall, give some example

vitalets commented 1 month ago

please give some suggestion what i need to implement in beforeall, give some example

In BeforeAll you can make some setup that needed once in each worker. I think BeforeFeature / AfterFeature will be more useful, so keep track of this issue https://github.com/vitalets/playwright-bdd/issues/219

imsunnymishra28 commented 2 weeks ago

@satyabrat105 is this issue resolved ?