当前位置: 首页 > 知识库问答 >
问题:

用量角器测试AngularJS-使用browser.wait()

苏弘盛
2023-03-14

我正在使用量角器为AngularJS应用程序开发一个自动化测试套件。

在开发测试脚本时,我一直使用browser.pause(),因此在执行脚本时,我必须手动告诉它继续执行测试的每一步。现在我很高兴我的测试能够正确执行,并且希望删除对browser.pause()的调用,这样我就可以让脚本自己运行到完成。

但是,我知道,我不能只删除对browser.pause()的调用,而不添加一些内容,允许测试在执行下一步之前暂停/等待浏览器加载(目前,在运行对browser.pause()的调用后,我告诉脚本继续所用的时间足以让浏览器加载下一步测试所需的元素)。

我尝试使用browser.wait()来实现这一点,将每个测试的最后一行作为参数传递给browser.wait(),同时传递一个超时值(即10秒)。例如:

it('should log the user in to the application', function() {
    browser.wait(loginAsUser(username, password), 10000);
});

测试最初只是:

browser.pause();

it('should log the user in to the application', function() {
    loginAsUser(username, password);
});

也就是说,在测试用例之外调用browser.pause()会导致浏览器在每个测试的每个步骤之间暂停。

function loginAsUser(username, password) {
    usernameInputField.sendKeys(username);
    passwordInputField.sendKeys(password);
    loginBtn.click();
}

我不明白为什么我会得到这个错误,因为wait条件是一个函数...它被赋予了我在上面定义的loginasuser()函数...?谁能给我解释一下我在这里做错了什么?

编辑

因此,问题似乎实际上出在我的其余测试中(即,login测试首先运行,然后在它之后依次运行许多其他测试)。

it('should display the Pages menu', function() {
    browser.waitForAngularEnabled(false);
    browser.actions().mouseMove(pagesMenuBtn).perform();
    expect(pageTagBrowserBtn.isDisplayed()).toBeTruthy(); 

    browser.actions().mouseMove(userCircle).perform();
    expect(pageTagBrowserBtn.isDisplayed()).toBeFalsy();
    browser.waitForAngularEnabled(true);
});

pagesmenubtn定义为一个全局变量,具有:

var pagesMenuBtn = element(by.linkText("Pages"));

因此,在运行下一个测试之前,我似乎需要给我的应用程序留出时间,以便在登录后加载页面,否则将找不到元素。

编辑

it('should display the Pages menu', function() {
    browser.waitForAngularEnabled(false);
    browser.wait(function() {
        pagesMenuBtn.isDisplayed().then(function(isDisplayed){
        /*  if(!isDisplayed) {
                console.log("Display Pages menu test returning false ");
                return false;
            }
            console.log("Display Pages menu test returning true "); */
            //return true;
            browser.actions().mouseMove(pagesMenuBtn).perform().then(function(isDisplayed){
                expect(pageTagBrowserBtn.isDisplayed()).toBeTruthy();
            });
            browser.actions().mouseMove(userCircle).perform().then(function(isDisplayed){
                expect(pageTagBrowserBtn.isDisplayed()).toBeFalsy();
            });
        });
    }, 5000);
});

好的,所以我再次更新了我的测试--现在看起来是这样的:

it('should display the Pages menu', function() {
    browser.waitForAngularEnabled(false);
    browser.wait(EC.visibilityOf(pagesMenuBtn), 5000).then(
        browser.actions().mouseMove(pagesMenuBtn).perform().then(function(){
            expect(pageTagBrowserBtn.isDisplayed()).toBeTruthy();
        })).then(
        browser.actions().mouseMove(userCircle).perform().then(function(){
            expect(pageTagBrowserBtn.isDisplayed()).toBeFalsy();
        }));
});

我这样做的目的是,浏览器将等待pagesmenubtn元素显示,然后,一旦显示了,光标将移动到按钮,一旦发生了这种情况,它将检查pagetagbrowserbtn元素是否显示(希望它返回一个'true'值)。然后,光标将移动到页面上的另一个元素(UserCircle),并再次检查是否显示了PageTagBrowserBTN(这次希望它返回一个'false'值)。

但是,当我现在运行测试时,它失败了,声明:

谁能给我解释一下这里出了什么问题?

共有1个答案

冉昊
2023-03-14

通常,您不应该等待一段硬编码的时间。

至少等待应该与一个预期的条件配对,以便在条件满足时尽快挣脱。

示例帮助器方法:

public static async waitForPresenceOf(element: ElementFinder, waitMs?: number): Promise<boolean> {
    return browser.wait(EC.presenceOf(element), waitMs || 5000).then(() => true, () => false);
}
import * as webdriver from 'selenium-webdriver';
import By = webdriver.By;


import {browser, element, protractor, by, WebElement, ElementFinder, ElementArrayFinder} from "protractor";

import * as _ from 'lodash';
import {expect} from "./asserts.config";

let EC = protractor.ExpectedConditions;

export default class BrowserHelper {

    public static isAuthenticated: boolean;


    public static async getCurrentUrl(): Promise<string> {
        return browser.getCurrentUrl();
    }

    public static async getDesignId(): Promise<string> {
        let url = await BrowserHelper.getCurrentUrl();

        let designId = '';

        let resources = _.split(url, '/');

        if (_.includes(resources, 'design')) {
            designId = resources[_.indexOf(resources, 'design') + 1];
        }

        return designId;
    }

    public static async scrollTo(x: number | string, y: number | string): Promise<void> {
        await browser.executeScript(`window.scrollTo(${x},${y});`);
    }

    public static async scrollToTop(): Promise<void> {
        await BrowserHelper.scrollTo(0, 0);
    }

    public static async scrollToBottom(): Promise<void> {
        await BrowserHelper.scrollTo(0, 'document.body.scrollHeight');
    }

    public static async scrollToLocator(locator: By | Function): Promise<void> {
        await browser.executeScript('arguments[0].scrollIntoView(false);', element(locator).getWebElement());
    }

    public static async scrollToElement(element: ElementFinder): Promise<void> {
        await browser.executeScript('arguments[0].scrollIntoView(false);', element);
    }

    public static async isElementPresent(element: ElementFinder, waitMs?: number): Promise<boolean> {
        return browser.wait(element.isPresent(), waitMs || 1000).then(() => true, () => false);
    }

    public static async getElement(locator: By | Function, waitMs?: number): Promise<ElementFinder | any> {

        let isPresent = await BrowserHelper.waitForPresenceOf(element(locator), waitMs || 1000);

        return isPresent ? element(locator) : undefined;
    }


    public static getParent(childElement: ElementFinder, levels?: number) {
        let xpath = levels ? '' : '..';

        for (let i = 1; i<=levels; i++) {
            xpath += (i<levels) ? '../' : '..';
        }

        return childElement.element(by.xpath(xpath));
    }

    public static async urlContains(str: string, waitMs?: number): Promise<boolean> {
        return browser.wait(EC.urlContains(str), waitMs || 5000).then(() => true, () => false);
    }

    public static async waitForPresenceOf(element: ElementFinder, waitMs?: number): Promise<boolean> {
        return browser.wait(EC.presenceOf(element), waitMs || 5000).then(() => true, () => false);
    }


    public static async waitForVisibilityOf(element: ElementFinder, waitMs?: number): Promise<boolean> {
        return browser.wait(EC.visibilityOf(element), waitMs || 5000).then(() => true, (e) => false);
    }

    public static async waitForInvisiblityOf(element: ElementFinder, waitMs?: number): Promise<any> {
        await browser.wait(EC.invisibilityOf(element), waitMs || 5000);
    }

    public static async isElementDisplayed(element: ElementFinder): Promise<boolean> {
        return element.isDisplayed().then(() => true, () => false);
    }

    public static async hasElement(locator: By | Function, waitMs?: number): Promise<boolean> {
        return !!BrowserHelper.getElement(locator, waitMs || 5000);
    }

    public static async hasClass(element: ElementFinder, className: string, waitMs?: number): Promise<boolean> {

        await BrowserHelper.isElementPresent(element, waitMs || 5000);

        return new Promise<boolean>((resolve) => {
            element.getAttribute('class').then(function (classes) {
                let hasClass = classes.split(' ').indexOf(className) !== -1;

                resolve(hasClass);
            });
        })
    }


    public static async sendKeys(locator: By | Function, keys: string, clear?: boolean, waitMs?: number): Promise<void> {

        let element: ElementFinder = await BrowserHelper.getElement(locator, waitMs);

        if (clear) {
            await element.clear();
        }

        await element.sendKeys(keys);
    }

    public static async click(locator: By | Function,  waitMs?: number): Promise<void> {
        let element = await BrowserHelper.getElement(locator, waitMs);
        await element.click();
    }

    public static async all(locator: By | Function, waitMs?: number): Promise<ElementFinder[]> {

        // verify presence while allowing it to take a short while for element to appear
        let isPresent: boolean = await BrowserHelper.waitForPresenceOf(element.all(locator).first(), waitMs || 5000);

        if (isPresent) {
            return element.all(locator);
        }

        throw new Error('Could not find Elements matching locator: ' + locator)
    }

    public static async allSub(parentElement: ElementFinder, locator: By | Function): Promise<ElementFinder[]> {
        // assuming element is present (e.g. found using getElement)
        return parentElement.all(locator);
    }

    public static async getElementText(element: ElementFinder): Promise<string> {
        return element.getText().then((text) => text, (e) => "");
    }

    public static async getText(locator: By | Function, waitMs?: number): Promise<string> {
        let element: ElementFinder = await BrowserHelper.getElement(locator, waitMs || 5000);

        return element.getText();
    }

    public static async allText(locator: By | Function, waitMs?: number): Promise<string[]> {
        let textElements: ElementFinder[] = await BrowserHelper.all(locator, waitMs || 5000);

        return BrowserHelper.elementText(...textElements);
    }

    public static async elementText(...elements: ElementFinder[]): Promise<string[]> {
        return Promise.all(_.map(elements, (element: ElementFinder) => {
            return BrowserHelper.getElementText(element);
        }));
    }


    public static async clickElementByLabel(clickablesLocator: By | Function, labelsLocator: By | Function, labelToClick: string,  waitMs?: number): Promise<void> {
        let labels: string[] = await BrowserHelper.allText(labelsLocator);

        // remove leading and trailing whitespaces
        labels = _.map<string, string>(labels, (label) => _.trim(label));


        let elements: ElementFinder[] = await BrowserHelper.all(clickablesLocator);

        expect(labels.length).to.be.eq(elements.length, `clickElementByLabel must have equal amount of clickables and labels`);

        let clickIndex = _.indexOf(labels, labelToClick);

        if (clickIndex >= 0) {
            let elem = elements[clickIndex];
            await BrowserHelper.waitForVisibilityOf(elem, waitMs || 5000);
            await elem.click();
        }
        else {
            throw new Error('Did not find Element with label:' + labelToClick)
        }
    }


    public static async clickElementIfPresent(locator: By | Function): Promise<void> {
        let isClickable = await BrowserHelper.waitForVisibilityOf(element(locator), 10);

        if (isClickable) {
            await element(locator).click();
        }

    }


    public static async getSessionKey(key: string): Promise<any> {
        return browser.driver.executeScript(`return window.sessionStorage.getItem("${key}");`);
    }

    // browser.driver.actions() does currently not properly add typings, so wrapping here for convenience
    public static actions(): webdriver.ActionSequence {
        return browser.driver.actions();
    }

    public static dragTo(to: webdriver.ILocation): webdriver.ActionSequence {

        let actions = BrowserHelper.actions();

        // reduce number of actions sent when testing via external selenium driver
        // if (process.env.E2E_EXTERNAL_SERVER) {
            actions.mouseMove({x: to.x-50, y: to.y});

            // ease in to trigger snap suggestion
            for (let i = 0; i < 10; i++) {
                actions.mouseMove({x: 5, y: 0});
            }
        // }
        // else {
        //
        //     let pxPerStep = 5;
        //
        //     let movedX = 0;
        //     let movedY = 0;
        //
        //     while (Math.abs(movedX) < Math.abs(to.x) || Math.abs(movedY) < Math.abs(to.y)) {
        //         let dx = 0;
        //         let dy = 0;
        //
        //         if (Math.abs(movedX) < Math.abs(to.x)) {
        //             dx = (to.x > 0) ? pxPerStep : -pxPerStep;
        //         }
        //
        //         if (Math.abs(movedY) < Math.abs(to.y)) {
        //             dy = (to.y > 0) ? pxPerStep : -pxPerStep;
        //         }
        //
        //         actions.mouseMove({x: dx, y: dy});
        //
        //         movedX += dx;
        //         movedY += dy;
        //     }
        // }

        return actions;
    }


}
it('should log the user in to the application', function(done) {
      BrowserHelper.sendKeys(usernameInputField, username)
        .then(function() { 
            return BrowserHelper.sendKeys(passwordInputField, password) 
        })
        .then(function() {
            return BrowserHelper.click(loginBtn)
        }).then(done)
});
it('should log the user in to the application', function() {
      return BrowserHelper.sendKeys(usernameInputField, username)
        .then(function() { 
            return BrowserHelper.sendKeys(passwordInputField, password) 
        })
        .then(function() {
            return BrowserHelper.click(loginBtn)
        })
});
 类似资料:
  • 问题内容: 我希望能够使用Protractor测试我的Angular应用程序。由于我使用RequireJS,因此无法在DOM中使用指令,这就是为什么我使用手动引导Angular的原因。 量角器输出如下错误输出: 错误:在页面上找不到Angular :重试以寻找超出的角度 然后,我意识到量角器文档中有一个警告: 对于使用angular.bootstrap手动引导的应用,Protractor不能立即使

  • 问题内容: 我有一个AngularJS应用,可使用GitHub上的oAuth SSO进行身份验证。我试图找出一种使用量角器和自动化登录测试的方法。我无法弄清楚如何检索非角度字段以及如何管理页面加载的等待时间,我的规范如下所示: 我这样运行命令: 如何正确加载身份验证页面,在字段中输入正确的信息,然后等待重定向回到我的Angularized应用程序(我可以从那里处理事情)。 我试图使用调试器跳入此代

  • 问题内容: 今天,我一直在努力处理以下这些量角器代码行: 放在语句后似乎总是失败。 错误输出太模糊: UnknownError:JavaScript错误:等待结果时卸载文档 那么,单击超链接并检查新URL的正确方法是什么? 这是我的测试: 如果我在点击链接之前, 测试将通过。 如果我点击链接后, 上面的输出在Protractor中引发错误。什么地方出了错? 问题答案: 不用调用,而是 等待URL更

  • 问题内容: 当前,我有一些只在特定条件下执行的测试用例。 使用以上语法将导致siteNotAllowingLogin通过此测试。我知道有一种解决方案可以将测试标记为PENDING,但是如果该测试不适用,我宁愿不显示该测试。 如果可能的话,我也想在测试用例中保留逻辑。因此,将if块保留在测试用例中。 如果不满足条件,如何跳过此测试的任何建议,以使其不会在结果中显示为PENDING或PASSED。 帮

  • 问题内容: 我目前已使用量角器进行了一些测试。这些测试正在从WebApi检索数据。但是,我想将WebApi调用指向一个真实但经过模拟的WebApi,该WebApi仅返回要用作测试数据的对象。 例如,当前请求是: 我想操纵此请求,因此测试将使用: 首先,我想编写一个可以更改请求标头的拦截器,但是我不知道如何使它与Protractor一起工作。我不能在量角器测试中直接使用角度,可以吗?如果可以的话,编

  • 问题内容: 我试图只是运行包含在Internet Explorer 上的量角器中的示例测试,并且不会太远:( 我尝试过将浏览器名称更改为“ internet Explorer”和 “ version”:11,并且我已注释掉directConnect(因为它对此有所抱怨 ) 我得到的错误是… WebDriverError:必须通过 webdriver.ie.driver系统属性设置驱动程序可执行文件