【问题标题】:fluent PageModel API with TestCafe流畅的 PageModel API 和 TestCafe
【发布时间】:2022-01-12 12:54:56
【问题描述】:

我正在尝试在 TestCafe 中为测试作者提供流畅的 PageModel api,例如:

await MyApp // a Page Model class instance  
  .navigateTo(xyz) // clicks a button to navigate to a specific part in my app  
  .edit() // clicks the edit button  
  .setField(abc, 12.34)  
  .save()  
  .changeStatus('complete'); 

我将所有单独的方法作为异步方法工作,可以单独等待,但这使得代码非常不可读,因此容易出错。

但是,无论我尝试以何种方式使 api 流畅,都会导致以下错误:

选择器不能隐式解析它所在上下文中的测试运行 应该被执行。如果您需要从 Node.js API 调用 Selector 回调,首先通过 Selector 的 .with({ boundTestRun: t }) 方法手动传递测试控制器。请注意,您无法执行 测试代码外的选择器。

制作流畅的异步 api 的诀窍是恕我直言,从异步函数切换到作为方法的常规函数​​,并让这些方法返回一个 thenable 'this' 值。并且为了防止 await 振荡,'then' 函数需要在调用后移除(然后在调用时重新安装

重现该问题的一个非常基本的示例如下所示:

import { Selector } from 'testcafe'

class MyPage {
    queue: [];

    async asyncTest() {
        return await Selector(':focus').exists;
    }

    queuedTest() {
        this.then = (resolve, reject) => {
            delete this.then; // remove 'then' once thenable gets called to prevent endless loop

            // calling hardcoded method, in a fluent api would processes whatever is on the queue and then resolve with something
            resolve(this.asyncTest());
        };

        // In a real fluent api impl. there would be code here to put something into the queue 
        // to execute once the 'then' method gets called
        // ...

        return this;
    }
}

fixture `Demo`
    .page `https://google.com`;


test('demo', async () => {
  const myPage = new MyPage();

  console.log('BEFORE')
    await myPage.asyncTest();
    console.log('BETWEEN')
    await myPage.queuedTest(); // Here it bombs out
    console.log('AFTER')
});

请注意,上面的示例并未展示 fluent api,它只是演示了通过“then”函数调用使用选择器的方法(恕我直言,这是创建 fluent api 的关键)导致上述错误。

注意:我知道错误的含义,并且建议将 .with({boundTestRun: t}) 添加到选择器中,但这会导致所需的样板代码并使事情难以维护。

任何想法表示赞赏 P.

【问题讨论】:

    标签: testing automation automated-tests e2e-testing testcafe


    【解决方案1】:

    在您的示例中,无法评估选择器,因为它无权访问测试控制器 (t)。您可以尽量避免在没有断言的情况下直接评估选择器。

    这是我的链式页面模型示例(基于这篇文章:Async Method Chaining in Node):

    页面模型:

    import { Selector, t } from 'testcafe';
    
    export class MyPage {
        constructor () {
            this.queue = Promise.resolve();
    
            this.developerName = Selector('#developer-name');
            this.submitButton  = Selector('#submit-button');
            this.articleHeader = Selector('#article-header');
        }
    
        _chain (callback) {
            this.queue = this.queue.then(callback);
    
            return this;
        }
    
        then (callback) {
            return callback(this.queue);
        }
    
        navigateTo (url) { 
            return this._chain(async () => await t.navigateTo(url));
        }
    
        typeName (name) { 
            return this._chain(async () => await t.typeText(this.developerName, name));
        }
    
        submit () {
            return this._chain(async () => await t.click(this.submitButton));
        }
    
        checkName (name) {
            return this._chain(async () => await t.expect(this.articleHeader.textContent).contains(name));
        }
    
        getHeader () {
            this._chain(async () => console.log(await this.articleHeader.textContent));
    
            return this;
        }
    }
    

    测试:

    import { MyPage } from "./page-model";
    
    fixture`Page Model Tests`;
    
    const page = new MyPage();
    
    test('Test 1', async () => {
        await page
            .navigateTo('http://devexpress.github.io/testcafe/example/')
            .typeName('John')
            .submit()
            .checkName('John')
            .getHeader();
    });
    

    【讨论】:

    • 一堆!尝试了很多东西,阅读了很多文章,但是看到你的例子和我的方法相比,最终让事情变得点击
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多