使用Page Objects
页面对象方法是一种流行的模式,它通过将web应用程序的页面或页面片段包装成对象来编写E2E测试。页面对象的目的是允许软件客户端做任何事情,并通过抽象出访问和操作页面所需的底层html操作来查看人们所能做的任何事情。
在版本v0.7的Nightwatch上为创建页面对象提供了一个增强的、更强大的界面,在之前的支持上有了显著的改进。在v0.7版本之前创建的页面对象仍然可以继续工作,但是建议升级到新版本。要使用新版本,你的页面对象必须包含elements或sections属相,否则,Nightwatch将会服从旧的。
配置Page Objects
要创建页面对象,只需创建具有描述页面的属性的对象。每个页面对象应该写在一个单独的文件里存在指定的文件夹中。Nightwatch根据配置文件page_objects_path设置来读取指定的文件夹(或文件夹)中的page objects。
page_objects_path属相也可以是一组文件夹,这样就可以在逻辑上将页面对象划分为更小的组。
Url 属性
你可以选择添加一个url属性,该属性指定页面的url。要导航到页面,你可以在page object上调用navigate方法。
URL通常定义为string:
module.exports = {
url: 'http://google.com',
elements: {}
};
它也可以是一个函数以防止URL是动态的。其中的一个用例是支持不同的测试环境。你可以创建一个在页面上下文中调用的函数,可以这样做:
module.exports = {
url: function() {
return this.api.launchUrl + '/login';
},
elements: {}
};
定义Elements
大多数情况下,你希望在你的页面上定义元素,这样你的测试将通过commands和assertions与之交互。这是使用elements属性简化并且你的所有元素都是在一个地方定义。特别是在更大的集成测试中,使用elements将会使测试代码DRY(Don’t Repeat Yourself)不啰嗦。
在css和xpath定位策略之间进行切换是在内部处理的,因此你不需要在测试中调用useXpath和useCss。默认的locateStrategy是css,但你也可以指定xpath:
module.exports = {
elements: {
searchBar: {
selector: 'input[type=text]'
},
submit: {
selector: '//[@name="q"]',
locateStrategy: 'xpath'
}
}
};
或者,如果你使用与默认策略相同的定位策略创建元素,您可以使用缩写:
module.exports = {
elements: {
searchBar: 'input[type=text]'
}
};
使用elements属性允许你在调用element commands和assertions (click等)时,使用“@”前缀而不是选择器来引用元素。
你还可以定义一个对象数组:
var sharedElements = {
mailLink: 'a[href*="mail.google.com"]'
};
module.exports = {
elements: [
sharedElements,
{ searchBar: 'input[type=text]' }
]
};
把elements和url放在一起,假设你有如下代码保存在google.js文件:
module.exports = {
url: 'http://google.com',
elements: {
searchBar: {
selector: 'input[type=text]'
},
submit: {
selector: '//[@name="q"]',
locateStrategy: 'xpath'
}
}
};
在你的测试中,你可以这样使用:
module.exports = {
'Test': function (client) {
var google = client.page.google();
google.navigate()
.assert.title('Google')
.assert.visible('@searchBar')
.setValue('@searchBar', 'nightwatch')
.click('@submit');
client.end();
}
};
定义Sections
有时在一个页面里定义sections是很有用的,sections可以做两件事:
- 在页面下面提供一个命名空间
- 提供元素级的嵌套,以便在一个sections中定义的任何元素都是DOM的父节点的后代
你可以使用sections属性创建分段:
module.exports = {
sections: {
menu: {
selector: '#gb',
elements: {
mail: {
selector: 'a[href="mail"]'
},
images: {
selector: 'a[href="imghp"]'
}
}
}
}
};
你在测试按照下面的方式来使用它:
module.exports = {
'Test': function (client) {
var google = client.page.google();
google.expect.section('@menu').to.be.visible;
var menuSection = google.section.menu;
menuSection.expect.element('@mail').to.be.visible;
menuSection.expect.element('@images').to.be.visible;
menuSection.click('@mail');
client.end();
}
};
注意,每个部分(除了expect断言)的每个command和assertion都返回该section用于链接。如果需要可以在其他sections下嵌套用于复杂的DOM结构。
嵌套page object sections的示例:
module.exports = {
sections: {
menu: {
selector: '#gb',
elements: {
mail: {
selector: 'a[href="mail"]'
},
images: {
selector: 'a[href="imghp"]'
}
},
sections: {
apps: {
selector: 'div.gb_pc',
elements: {
myAccount: {
selector: '#gb192'
},
googlePlus: {
selector: '#gb119'
}
}
}
}
}
}
};
在你的测试里直接使用嵌套section,示例:
module.exports = {
'Test': function (client) {
var google = client.page.google();
google.expect.section('@menu').to.be.visible;
var menuSection = google.section.menu;
var appSection = menuSection.section.apps;
menuSection.click('@appSection');
appSection.expect.element('@myAccount').to.be.visible;
appSection.expect.element('@googlePlus').to.be.visible;
client.end();
}
};
编写Commands
你可以使用commands属性向页面对象添加命令,这是一种很有用的方法它可以封装在一个或多个测试中可能存在的页面的逻辑。
Nightwatch将在页面或部分的上下文中调用该命令。客户端命令比如pause可以通过this.api获得。对于链接,每个函数都应该返回page object或section。
在本例中,command是用来封装单击submit按钮的逻辑:
var googleCommands = {
submit: function() {
this.api.pause(1000);
return this.waitForElementVisible('@submitButton', 1000)
.click('@submitButton')
.waitForElementNotPresent('@submitButton');
}
};
module.exports = {
commands: [googleCommands],
elements: {
searchBar: {
selector: 'input[type=text]'
},
submitButton: {
selector: 'button[name=btnG]'
}
}
};
那么测试就可以简单的这么来写:
module.exports = {
'Test': function (client) {
var google = client.page.google();
google.setValue('@searchBar', 'nightwatch')
.submit();
client.end();
}
};