AngularJS迁移至Angular 5后Protractor测试适配问题求助
我之前经手过AngularJS到Angular 5的UI库迁移,也踩过不少Protractor测试适配的坑,结合你贴的新旧代码,给你梳理下核心的调整方向和具体修改点:
一、定位策略的核心调整
Angular 5的内容投影机制(ng-content)和AngularJS的ng-transclude差异很大,直接导致测试里的元素选择器大部分需要修改:
- 内容区域选择器:原来AngularJS测试里用的
.rt-card__content > rt-card-content会失效——因为Angular的ng-content会把投影内容直接注入到.rt-card__content容器内,不再保留rt-card-content标签。需要改成直接定位容器本身:.rt-card__content。 - 链接区域选择器:原代码里的
.rt-card__footer > rt-card-link > a要替换成.rt-card__footer > rt-card-footer > a,因为你新的Angular模板里用的投影选择器是rt-card-footer(对应旧代码的rtCardLink)。 - 属性绑定验证:虽然
data-rt-id的生成逻辑(rtId + 'Title')和旧代码({{::$ctrl.rtId}}Title)一致,但如果测试里要定位带data-rt-id的元素,确保选择器格式正确(比如[data-rt-id='default-cardTitle'])。
二、测试代码的API与风格升级
Angular 5对应的Protractor版本已经支持ES6+的异步语法,建议把旧的Promise回调改成async/await,让测试代码更简洁易读:
修改测试组件类的方法
比如把原来的getTitleText()从回调风格改成async/await:
// 旧代码 getTitleText(): promise.Promise<string> { return this.component.element(by.css(this.titleElementSelector)).getText().then((text) => { return text ? text.trim() : ''; }); } // 新代码 async getTitleText(): Promise<string> { const text = await this.component.element(by.css(this.titleElementSelector)).getText(); return text ? text.trim() : ''; }
修改测试用例的执行逻辑
所有测试步骤都要改成异步等待,避免Protractor的同步问题:
// 旧代码 beforeAll(() => { page = new CardPage(); }); // 新代码 beforeAll(async () => { page = new CardPage(); await browser.get('#/components/card'); }); // 测试用例示例 it('should contain the correct title text', async () => { expect(await page.defaultCard.getTitleText()).toEqual('Time off', `Title text is not correct`); });
三、Protractor与Angular 5的同步配置
确保你的Protractor配置文件(protractor.conf.js)适配Angular 5:
- 检查
rootElement配置是否指向你的Angular应用根组件(比如app-root),Protractor需要通过它来识别Angular应用并同步异步操作。 - 如果测试中用到了非Angular的第三方组件,需要临时禁用Angular同步:
await browser.waitForAngularEnabled(false); // 执行非Angular元素操作 await browser.waitForAngularEnabled(true);
四、针对你提供代码的完整修改示例
测试用的CardComponent类修改
import {element, by, ElementFinder} from 'protractor'; export interface ICard { isPresent(): Promise<boolean>; getTitleText(): Promise<string>; getContentHtml(): Promise<string>; getLinkText(): Promise<string>; getLinkUrl(): Promise<string>; } export class CardComponent implements ICard { private readonly titleElementSelector: string = '.rt-card__title'; private readonly contentElementSelector: string = '.rt-card__content'; // 修正选择器 private readonly linkElementSelector: string = '.rt-card__footer > rt-card-footer > a'; // 修正选择器 private component: ElementFinder; constructor(selector: string) { this.component = element(by.css(`rt-card${selector}`)); } async isPresent(): Promise<boolean> { return this.component.isPresent(); } async getTitleText(): Promise<string> { const text = await this.component.element(by.css(this.titleElementSelector)).getText(); return text ? text.trim() : ''; } async getContentHtml(): Promise<string> { const html = await this.component.element(by.css(this.contentElementSelector)).getAttribute('innerHTML'); return html.trim(); } async getLinkText(): Promise<string> { return this.component.element(by.css(this.linkElementSelector)).getText(); } async getLinkUrl(): Promise<string> { return this.component.element(by.css(this.linkElementSelector)).getAttribute('href'); } }
功能测试用例修改
import CardPage from './card.page'; let page; describe('Card', () => { beforeAll(async () => { page = new CardPage(); await browser.get('#/components/card'); }); it('should be available on the page', async () => { expect(await page.defaultCard.isPresent()).toBe(true, `Can't find the default card`); }); it('should contain the correct title text', async () => { expect(await page.defaultCard.getTitleText()).toEqual('Time off', `Title text is not correct`); }); it('should contain the correct content', async () => { expect(await page.defaultCard.getContentHtml()).toEqual(`<span>0 hours available</span>`, `Content HTML is not correct`); }); it('should contain the correct link', async () => { expect(await page.defaultCard.getLinkText()).toEqual(`Go to time off`, `Link text is not correct`); expect(await page.defaultCard.getLinkUrl()).toEqual(`http://www.google.com/`, `Link URL is not correct`); }); });
额外注意事项
- 升级Protractor到支持Angular 5的版本(建议v5.x及以上),避免版本兼容问题。
- 如果遇到元素超时未出现的问题,可使用Protractor的
ExpectedConditions来等待元素:
import {ExpectedConditions} from 'protractor'; await browser.wait(ExpectedConditions.presenceOf(this.component), 5000, 'Card component not found');
内容的提问来源于stack exchange,提问作者rf2018
相关产品推荐
相关产品推荐

