Python中的Nose2入门
深入了解Nose框架的后继者,Nose2。与Nose一样,Nose2扩展了unittest框架,可广泛用于Selenium测试自动化。
Nose2是Python中流行的测试运行器,可以检测项目中的单元测试并执行它们。如果你熟悉unittest --Python的标准库,并且比Python中的其他测试自动化框架更喜欢同样的东西--那么你应该简单了解一下Nose2 Python。
Nose2 Python是基于unittest ,并通过其丰富的插件生态系统为该框架增加了更多价值。简单地说,Nose2是unittest 模块的一个扩展。我们早期的博客关于Selenium Python Nose教程对Nose的旧版本(1.3.7)进行了深入的研究。它仍然被一定比例的开发和测试兄弟会所使用。
在这篇博客中,我们将研究Nose2框架,它是Nose的继承者。在这个Python Nose教程结束时,你将会很舒服地利用Nose2中的插件来增强你的Selenium Python测试经验,实现Selenium测试自动化。
Nose2框架简介
Nose2是Nose的后继者,是为了扩展unittest ,以简化测试过程而开发的。Nose2是基于unittest2的插件分支。
与Nose和unittest 相比,Nose2提供了一个更好的插件API,并简化了内部接口和流程。有许多插件被内置到Nose2模块中,这些插件被默认加载。一些默认加载的主要插件有助于测试的参数化,将测试夹具组织成层,捕捉日志信息,提供测试覆盖率报告,等等。
默认情况下,Nose2不支持并行测试执行,这是测试自动化中广泛使用的一个功能。然而,在Nose2中加载插件并不困难,因为插件模块的名字可以毫不费力地添加到配置文件[unittest]部分的插件列表中。也有一个选项可以在命令行中用-plugin参数传递插件模块。
因此,用Nose2在多个进程中并行运行测试,只是通过前面提到的任何一个选项来启用该插件而已Nose2在GitHub上提供。下面是关于Github上项目的一些更多细节。
| 叉子 - 130 | 使用的人 - 3.4K |
| 列为星级 - 674 | 贡献者 - 61 |
| 发布 - 28 | 提交 - 990 |
如何安装Nose2框架
在我们开始学习这个Python Nose教程之前,你需要在你的系统中安装Nose2框架(如果你还没有的话)。Nose2可以通过在终端执行以下命令来安装。
pip install nose2
从下面的安装快照中可以看出,Nose2的0.9.2版本已经安装。
可以通过在实现中使用import nose2来导入nose2包。如果要使用某个包中的特定模块,同样可以使用导入。
from nose2.<package_name> import <module_name>
如何执行Nose2 Python测试
由于Nose2与Nose不同,Nose2中触发测试的命令也不同。下面是在Nose2中执行测试的命令。
nose2 --verbose <filename_without_.py>
例如,如果包含Nose2测试的文件名是Test_Nose2_example.py,用于执行里面的测试的命令应该是。
nose2 --verbose Test_Nose2_example
Nose2测试运行器提供了许多捕捉日志、报告等的选项,可以在执行测试时在控制台传递相同的内容来使用。
Nose2中的测试发现
模块(或文件)和测试用例所遵循的以test_ 开始的命名法在 Nose2 中也适用。封装测试方法的测试类应该以Test 开始。
Nose2提供了一个插件,实现了自动测试模块的发现。该插件在包和目录中寻找模块(或测试文件),其名称以test开头。然后它为所有的发现触发loadTestsFromModule() 钩子,允许其他插件加载实际的测试。你可以在Nose2中找到更多关于基于发现的测试加载器的细节。
Nose2框架的使用实例
为了演示Nose2框架在这个Python Nose教程中的用法,我们使用了先前在演示Nose时使用的同一个例子,即Google搜索LambdaTest,并对第一个测试结果执行点击。
import unittest
from selenium import webdriver
import time
from time import sleep
from selenium.webdriver.common.by import By
class ChromeSearch(unittest.TestCase):
def test_search_lambdatest_chrome(self):
self.driver = webdriver.Chrome()
self.driver.get('https://www.google.com')
self.driver.maximize_window()
title = "Google"
assert title == self.driver.title
search_text = "LambdaTest"
search_box = driver.find_element(By.XPATH, "//input[@name='q']")
search_box.send_keys(search_text)
# Using Sleep is not a good programming practice
# Only used here for demonstration purpose
time.sleep(5)
search_box.submit()
time.sleep(5)
# Click on the LambdaTest HomePage Link
title = "Most Powerful Cross Browser Testing Tool Online | LambdaTest"
lt_link = driver.find_element(By.XPATH, "//h3[.='LambdaTest: Most Powerful Cross Browser Testing Tool Online']")
lt_link.click()
time.sleep(10)
assert title == driver.title
time.sleep(2)
# Release the resources in the teardown function
print("TearDown initiated")
driver.quit()
if __name__ == '__main__':
import nose2
nose2.main()
这个实现与我们在使用unittest 模块时可能使用的几乎相同。主要的(但也是可选的)是nose2.main() 。它的使用方式与unittest.main() 相同,目的是在一个单一的模块中运行这些测试。
if __name__ == '__main__':
import nose2
nose2.main()
Nose2 Python中的参数化测试
不像Nose需要安装一个单独的包(即nose-parameterized)来实现测试参数化,Nose2 Python比Nose支持更多种类的参数化和生成器测试。它支持在测试类、测试函数和unittest``TestCase 子类中的测试生成器。
这个插件实现了以下函数,以支持从参数化测试函数和方法中加载测试。
对于函数或测试用例方法的参数化,使用nose2.tools.params() 。我们将在这个Python Nose教程中使用同样的内容。
nose2.tools.params(*paramList)
列表中的参数可以是简单的值或图元。例如,在本地Selenium网格上执行Selenium Python测试(使用参数化)时,我们使用简单的值。另一方面,当在基于云的Selenium网格(如LambdaTest)上执行Selenium测试自动化时,我们会使用图元(即浏览器名称、浏览器版本和操作系统的组合)进行测试参数化。
关于参数化测试的Nose2 Python插件的更多细节可在这里找到。
我们移植了Selenium Python Nose教程中使用的例子,其中LambdaTest ToDo应用程序针对三个不同的浏览器进行了测试。Firefox、Microsoft Edge和Chrome。
- 导航到LambdaTest样本应用程序的URL。
- 选择前两个复选框。
- 将 "Happy Testing at LambdaTest "发送到id = sampletodotext的文本框中。
- 点击 "添加 "按钮,验证文本是否已被添加。
实施
import unittest
from selenium import webdriver
import time
from time import sleep
from selenium.webdriver.common.by import By
# module for importing params functionality
from nose2.tools import params
class LT_Parameterized_local_test(unittest.TestCase):
@params("Firefox", "Chrome", "MicrosoftEdge")
def test_to_do_app(self, browserName):
if (browserName == "Chrome"):
print("Test on Chrome Browser initiated")
self.driver = webdriver.Chrome()
elif (browserName == "MicrosoftEdge"):
print("Test on Edge Browser initiated")
# Set the Path accordingly
self.driver = webdriver.Edge("C:\\EdgeDriver\\MicrosoftWebDriver.exe")
elif (browserName == "Firefox"):
print("Test on Firefox initiated")
self.driver = webdriver.Firefox()
self.driver.get('https://lambdatest.github.io/sample-todo-app/')
self.driver.maximize_window()
self.driver.find_element(By.NAME, "li1").click()
self.driver.find_element(By.NAME, "li2").click()
title = "Sample page - lambdatest.com"
assert title == self.driver.title
sample_text = "Happy Testing at LambdaTest"
email_text_field = self.driver.find_element(By.ID, "sampletodotext")
email_text_field.send_keys(sample_text)
time.sleep(5)
self.driver.find_element(By.ID, "addbutton").click()
time.sleep(5)
assert self.driver.find_element(By.XPATH, "//span[.='Happy Testing at LambdaTest']").text == sample_text
def tearDown(self):
# Close the browser.
self.driver.quit()
if __name__ == '__main__':
import nose2
nose2.main()
代码演练
由于测试方法必须被参数化,我们从nose2.tools 包中导入params函数到当前文件。
from nose2.tools import params
ToDo App需要测试的浏览器在@params 装饰器下被作为参数传递。由于测试是在本地Selenium网格上进行的,所以浏览器的名称被作为值传递。
class LT_Parameterized_local_test(unittest.TestCase):
@params("Firefox", "Chrome", "MicrosoftEdge")
def test_to_do_app(self, browserName):
测试方法(即test_to_do_app )接受browserName 作为输入参数,在这个Python Nose教程的整个例子中,同样用于Selenium测试自动化。
def test_to_do_app(self, browserName):
if (browserName == "Chrome"):
print("Test on Chrome Browser initiated")
self.driver = webdriver.Chrome()
elif (browserName == "MicrosoftEdge"):
print("Test on Edge Browser initiated")
# Set the Path accordingly
self.driver = webdriver.Edge("C:\\EdgeDriver\\MicrosoftWebDriver.exe")
elif (browserName == "Firefox"):
print("Test on Firefox initiated")
self.driver = webdriver.Firefox()
self.driver.get('https://lambdatest.github.io/sample-todo-app/')
...............................................
...............................................
其余的实现包含实际的测试案例逻辑,并使用Selenium WebDriver的API来定位所需的网络元素并对这些元素执行操作。由于这部分的实现是独立于测试框架的,我们不会在这个Python Nose教程中涉及这些方面。你可以参考我们早期关于Selenium WebDriver的博客,在那里我们已经更详细地介绍了这些方面。
执行
下面的命令用于触发测试的执行。
nose2 --verbose Nose_LT_Parameterized_local_Test_2
Nose_LT_Parameterized_local_Test_2 是测试名称。测试案例所在的文件名是Nose_LT_Parameterized_local_Test_2.py。这里是输出快照。
Nose2中的固定装置
Nose2支持类、模块和测试(或方法)级别的夹具,这也是其他流行的框架如PyTest的情况。为了演示Nose2中固定装置的使用,我们在前面的例子中添加了setUp() 和tearDown() 固定装置。
import unittest
from selenium import webdriver
import time
from time import sleep
from selenium.webdriver.common.by import By
from nose2.tools import params
class ChromeSearch(unittest.TestCase):
def setUp(self):
print("setUp initiated")
self.driver = webdriver.Chrome()
self.driver.maximize_window()
def test_search_lambdatest_chrome(self):
self.driver.get('https://www.google.com')
title = "Google"
assert title == self.driver.title
search_text = "LambdaTest"
# search_box = driver.find_element_by_xpath("//input[@name='q']")
search_box = self.driver.find_element(By.XPATH, "//input[@name='q']")
search_box.send_keys(search_text)
# Using Sleep is not a good programming practice
# Only used here for demonstration purpose
time.sleep(5)
search_box.submit()
time.sleep(5)
# Click on the LambdaTest HomePage Link
title = "Most Powerful Cross Browser Testing Tool Online | LambdaTest"
lt_link = self.driver.find_element(By.XPATH, "//h3[.='LambdaTest: Most Powerful Cross Browser Testing Tool Online']")
lt_link.click()
time.sleep(10)
assert title == self.driver.title
time.sleep(2)
def test_to_do_app(self):
self.driver.get('https://lambdatest.github.io/sample-todo-app/')
self.driver.maximize_window()
self.driver.find_element(By.NAME, "li1").click()
self.driver.find_element(By.NAME, "li2").click()
title = "Sample page - lambdatest.com"
assert title == self.driver.title
sample_text = "Happy Testing at LambdaTest"
email_text_field = self.driver.find_element(By.ID, "sampletodotext")
email_text_field.send_keys(sample_text)
time.sleep(5)
self.driver.find_element(By.ID, "addbutton").click()
time.sleep(5)
assert self.driver.find_element(By.XPATH, "//span[.='Happy Testing at LambdaTest']").text == sample_text
def tearDown(self):
# Close the browser
print("TearDown initiated")
self.driver.quit()
if __name__ == '__main__':
import nose2
nose2.main()
设置的夹具(即setUp() )在类(包含夹具)中的任何测试方法被执行之前被调用。另一方面,在执行每个测试方法(在该类下实现的)之后调用拆解的夹具(即tearDown() )。
这里显示的是执行截图,它显示setUp/tearDown 为每个测试用例被调用。
将测试夹具组织成层
在Python Nose教程的这一部分,我们看一下层,这是Nose2(0.4版)中新引入的概念。与传统的夹具相比,使用层的主要优点是在组织夹具方面的灵活性。其目的是与Zope的testrunner 所用的层相兼容。
下面是一些通过使用层可以实现的事情。
- 通过在包的所有测试用例中共享一个层来实现包级的固定程序
- 创建一个比可用级别(测试、类和模块)更深的夹具树
- 在不同模块的测试中共享夹具,而不需要多次运行。
下面是一个简单的演示,一个类至少实现了一个setUp 方法。
class Layer(object):
@classmethod
def setUp(cls):
# .........................
tearDown,testSetUp, 和testTearDown 方法也可以实现为classmethods 。下面是对各层可用方法的简短描述。
setUp(cls)**:**在任何属于该层的测试被执行之前被调用testSetUp(cls [, test]): 在执行属于该层(及其子层)的每个测试之前被调用;该方法可以接受一个参数,其中测试用例实例被传递给该方法tearDown(cls): 在任何属于该层的测试被执行后被调用;如果该层没有耦合的setUp方法,或者setUp,由于某些异常而不能运行,它将不会被调用。testTearDown(cls [, test])**:**在每个属于该层(和它的子层)的测试被执行后被调用;只有当该层定义了一个setUp(或testSetUp)测试方法并且该方法运行没有任何问题时才会被调用。
为了将一个层分配给一个测试用例,你需要设置测试用例的层属性,如下所示。
class Test(unittest.TestCase):
layer = Layer
在实现中使用层之前,你必须在执行Selenium自动化测试的地方的nose2.cfg(或unittest.cfg)中加载**nose2.plugins.**layer这个插件。
unittest.cfg (或nose.cfg)
[unittest]
plugins = nose2.plugins.layers
[test-result]
always-on = True
descriptions = True
[layer-reporter]
always-on = True
colors = True
Nose2中的固定装置(带层)演示
在这个Python Nose教程中,对于固定装置的演示,我们使用基于云的Selenium网格,因为与本地Selenium网格相比,它是一个更可扩展的选择。LambdaTest就是这样一个基于云的Selenium网格,它提供了在2000多种不同的浏览器、设备模拟器和操作系统的组合上运行Selenium自动化测试的设施。
将在本地Selenium网格上运行的现有实现移植到LambdaTest这样的基于云的Selenium网格上,并不涉及学习曲线。从投资和可扩展性的角度来看,在基于云的Selenium网格上使用Nose2 Python进行Selenium Python测试是一个更好的选择。
在LambdaTest上创建一个账户后,你应该注意配置文件部分的用户名和访问密钥,以便在LambdaTest上访问Selenium网格。你可以访问 "仪表板 "部分,以获得有关现有测试和早期在该平台上执行的测试的详细信息。
先前演示的LambdaTest上的ToDo应用程序是由三个不同的浏览器和操作系统组合执行的。
- Windows 10上的Chrome 71.0
- Windows 10上的Firefox 64.0
- macOS Mojave上的Safari 12.0
LambdaTest上的能力生成器被用来生成所需的浏览器和平台能力。下面显示的是为Chrome 71.0和Windows 10组合生成的浏览器功能快照。
实施
import unittest
from selenium import webdriver
import time
from time import sleep
import urllib3
import warnings
from selenium.webdriver.common.by import By
# module for importing params functionality
from nose2.tools import params
user_name = "registered_email_address"
app_key = "pass_key"
#Set capabilities for testing on Chrome
ch_caps = {
"build" : "Nose2 Fixture Testing using Chrome on Windows Environment",
"name" : "Nose2 Fixture Testing on Chrome using Selenium Grid Environment",
"platform" : "Windows 10",
"browserName" : "Chrome",
"version" : "71.0",
"selenium_version" : "3.13.0",
"chrome.driver" : 2.42
}
#Set capabilities for testing on Firefox
ff_caps = {
"build" : "Nose2 Fixture using Firefox on Windows Environment",
"name" : "Nose2 Fixture on Firefox using Selenium Grid Environment",
"platform" : "Windows 10",
"browserName" : "Firefox",
"version" : "64.0",
}
#Set capabilities for testing on Safari
saf_caps = {
"build" : "Nose2 Fixture using Safari on macOS Mojave Environment",
"name" : "Nose2 Fixture on Safari using Selenium Grid Environment",
"platform" : "macOS Mojave",
"browserName" : "Safari",
"version" : "12.0",
}
class Layer(object):
@classmethod
def setUp(cls):
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
print("Inside setUp")
@classmethod
def testTearDown(cls):
global driver
print("Inside tearDown")
# Close the browser.
driver.quit()
class LT_Fixture_Test(unittest.TestCase):
layer = Layer
# def setUp(self):
# urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
@params((ch_caps), (ff_caps), (saf_caps))
def test_lambdatest_todo_app(self, caps):
global driver
# Details can be sourced from https://automation.lambdatest.com/
remote_url = "https://" + user_name + ":" + app_key + "@hub.lambdatest.com/wd/hub"
self.driver = webdriver.Remote(command_executor = remote_url, desired_capabilities = caps)
driver = self.driver
self.driver.get('https://lambdatest.github.io/sample-todo-app/')
self.driver.maximize_window()
self.driver.get('https://lambdatest.github.io/sample-todo-app/')
self.driver.maximize_window()
self.driver.find_element(By.NAME, "li1").click()
self.driver.find_element(By.NAME, "li2").click()
title = "Sample page - lambdatest.com"
assert title == self.driver.title
sample_text = "Happy Testing at LambdaTest"
email_text_field = self.driver.find_element(By.ID, "sampletodotext")
email_text_field.send_keys(sample_text)
time.sleep(5)
self.driver.find_element(By.ID, "addbutton").click()
time.sleep(5)
assert self.driver.find_element(By.XPATH, "//span[.='Happy Testing at LambdaTest']").text == sample_text
# def tearDown(self):
# # Close the browser.
# print("Inside tearDown")
# self.driver.quit()
if __name__ == '__main__':
import nose2
nose2.main()
代码演练
1.在实施的开始阶段,导入必要的Nose2 Python包,例如nose2.tools 。
from nose2.tools import params
2.2.LambdaTest的配置文件部分中的用户名和密码的组合被用来访问LambdaTest网格的URL。
remote_url = "https://" + user_name + ":" + app_key + "@hub.lambdatest.com/wd/hub"
3.3.Selenium WebDriver API使用在线生成器生成的URL、浏览器和平台功能。
driver = webdriver.Remote(command_executor = remote_url, desired_capabilities = capabilities)
4.最重要的部分是Layer 类的实现,该类下的setUp/testTearDown 方法被实现。
class Layer(object):
@classmethod
def setUp(cls):
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
print("Inside setUp")
@classmethod
def testTearDown(cls):
global driver
print("Inside tearDown")
# Close the browser.
driver.quit()
方法setUp() 将在测试方法类的开头被调用。另一方面,testTearDown() 将在属于该类的每个测试方法的末尾被调用。清理活动(与被测试的浏览器有关)在该方法中进行,并且必须在每个测试方法之后执行。
而不是testTearDown ,如果我们使用tearDown 方法,它将只被调用一次(即在测试方法类的最后)。我们使用testTearDown ,因为在每个测试案例之后,浏览器资源需要被释放。setUp/testTearDown 方法包含在@classmethod 装饰器下;否则,它们在测试执行过程中不会被调用。
5.5.层需要被分配给测试用例。因此,测试用例的层属性是用下面的实现来设置的。
class LT_Fixture_Test(unittest.TestCase):
layer = Layer
6.浏览器和操作系统的能力被传递给@params 装饰器,因为Selenium Python测试是针对所需能力的每个组合进行的。
ch_caps = {
"build" : "Nose2 Fixture Testing using Chrome on Windows Environment",
"name" : "Nose2 Fixture Testing on Chrome using Selenium Grid Environment",
"platform" : "Windows 10",
"browserName" : "Chrome",
"version" : "71.0",
"selenium_version" : "3.13.0",
"chrome.driver" : 2.42
}
.........................................
.........................................
class LT_Fixture_Test(unittest.TestCase):
layer = Layer
.........................................
.........................................
@params((ch_caps), (ff_caps), (saf_caps))
def test_lambdatest_todo_app(self, caps):
global driver
# Details can be sourced from https://automation.lambdatest.com/
remote_url = "https://" + user_name + ":" + app_key + "@hub.lambdatest.com/wd/hub"
self.driver = webdriver.Remote(command_executor = remote_url, desired_capabilities = caps)
其余的实现独立于测试运行器,因为它们利用Selenium WebDriver的API来定位和执行那些网络元素上的动作,如点击等。
执行
下面的命令被用来执行。
nose2 --verbose Nose_LT_Fixtures_Test_4
这里,Nose_LT_Fixtures_Test_4.py是包含测试实现的文件。下面显示的是LambdaTest平台上的自动化标签的截图。
下面是表示测试完成的截图。所有的测试案例都是连续执行的,即一个接一个。
终端显示,setUp 方法只运行一次,而testTearDown 方法是在完成每个测试方法后执行的。
Nose2中的并行测试
Selenium中的并行测试让你在不同的测试环境中同时运行相同的测试。在Python Nose教程的这一部分,我们看看如何在Nose2 Python中实现并行测试。
mp 插件是在Nose2 0.3版本中引入的,用于实现测试在多个进程中的分布。尽管并行测试的执行导致了执行速度的提高(因为测试是并行运行的),但这种提高在IO绑定的测试中比CPU绑定的测试更明显。你还需要检查mp插件的使用是否与其他插件冲突,这些插件的设计并不是与它一起工作的。
下面是在Nose2 Python中实现并行化的一些方法。
- 通过在配置文件中的
[unittest]部分包含同样的内容来激活mp插件。
[unittest]
plugins = nose2.plugins.mp
- 或者你可以通过用
–plugin命令行选项传递该模块来实现。
nose2 --plugin=nose2.plugin.mp
激活mp 插件后,你必须配置可以并行运行的进程数量。这可以通过-N 选项来实现。
nose2 -N <num_parallel_processes>
这也可以通过在配置文件的[multiprocess] 部分设置进程的数量来实现。
[multiprocess]
processes = <num_parallel_processes>
需要注意的是,在启用并行时,测试不会以相同的顺序运行。如果测试套件有相互依赖关系(这本身就不是一个好的编程实践),那么如果启用mp 插件,测试可能会随机失败。还有其他一些测试作者的基本准则,你可以在官方文档中找到,标题是用Nose2并行运行测试。
为了演示Nose2 Python中的并行测试执行,在这个Python Nose教程中,我们在六个不同的浏览器和操作系统组合中执行两个测试场景。
测试方案1
- 导航到LambdaTest示例应用程序的URL。
- 选择前两个复选框。
- 向id = sampletodotext的文本框发送 "Happy Testing at LambdaTest"。
- 点击 "添加 "按钮,验证文本是否已被添加。
测试案例2
- 导航到谷歌主页。
- 搜索 "Lambdatest"。
- 找到第一个搜索结果,并点击同一个。
- 如果打开的窗口的标题与预期的标题不一致,则断言。
两个测试方案都是针对以下浏览器和操作系统组合执行的。
- Chrome(71.0),Windows 10
- Firefox (64.0), Windows 10
- Safari (12.0), macOS Mojave
对于并行的Selenium Python测试,我们将使用旧式的固定装置而不是层,因为使用层的测试套件与本Python Nose教程中的多进程(或mp )插件不兼容。不要尝试层和多进程的组合,因为它不会工作。
实施(测试用例1)
# Layer and Multiprocessor plugins are not compatible hence, the old-style fixtures are used
# https://docs.nose2.io/en/latest/plugins/layers.html#mixing-layers-and-multiprocess-testing
import unittest
from selenium import webdriver
import time
from time import sleep
import urllib3
import warnings
from selenium.webdriver.common.by import By
# module for importing params functionality
from nose2.tools import params
user_name = "user-name"
app_key = "access-key"
#Set capabilities for testing on Chrome
ch_caps = {
"build" : "Nose Testing using Chrome on Windows Environment(1)",
"name" : "Nose Testing on Chrome using Selenium Grid Environment(1)",
"platform" : "Windows 10",
"browserName" : "Chrome",
"version" : "71.0",
"selenium_version" : "3.13.0",
"chrome.driver" : 2.42
}
#Set capabilities for testing on Firefox
ff_caps = {
"build" : "Nose Testing using Firefox on Windows Environment(2)",
"name" : "Nose Testing on Firefox using Selenium Grid Environment(2)",
"platform" : "Windows 10",
"browserName" : "Firefox",
"version" : "64.0",
}
#Set capabilities for testing on Safari
saf_caps = {
"build" : "Nose Testing using Safari on macOS Mojave Environment(3)",
"name" : "Nose Testing on Safari using Selenium Grid Environment(3)",
"platform" : "macOS Mojave",
"browserName" : "Safari",
"version" : "12.0",
}
# class Layer(object):
# @classmethod
# def setUp(cls):
# urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# print("Test Mod 1 - Inside setUp")
# @classmethod
# def testTearDown(cls):
# global driver
# # Close the browser.
# driver.quit()
class Test_Parallel_test_1(unittest.TestCase):
# layer = Layer
def setUp(self):
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
print("Test Mod 1 - setUp initiated")
@params((ch_caps), (ff_caps), (saf_caps))
def test_lambdatest_todo_app(self, caps):
# Details can be sourced from https://automation.lambdatest.com/
remote_url = "https://" + user_name + ":" + app_key + "@hub.lambdatest.com/wd/hub"
self.driver = webdriver.Remote(command_executor = remote_url, desired_capabilities = caps)
self.driver.get('https://lambdatest.github.io/sample-todo-app/')
self.driver.maximize_window()
self.driver.find_element(By.NAME, "li1").click()
self.driver.find_element(By.NAME, "li2").click()
title = "Sample page - lambdatest.com"
assert title == self.driver.title
sample_text = "Happy Testing at LambdaTest"
email_text_field = self.driver.find_element(By.ID, "sampletodotext")
email_text_field.send_keys(sample_text)
time.sleep(5)
self.driver.find_element(By.ID, "addbutton").click()
time.sleep(5)
assert self.driver.find_element(By.XPATH, "//span[.='Happy Testing at LambdaTest']").text == sample_text
def tearDown(self):
# Close the browser
print("Test Mod 1 - TearDown initiated")
self.driver.quit()
if __name__ == '__main__':
import nose2
nose2.main()
实现 (测试用例2)
# Layer and Multiprocessor plugins are not compatible hence, the old-style fixtures are used
# https://docs.nose2.io/en/latest/plugins/layers.html#mixing-layers-and-multiprocess-testing
import unittest
from selenium import webdriver
import time
from time import sleep
import urllib3
import warnings
from selenium.webdriver.common.by import By
# module for importing params functionality
from nose2.tools import params
user_name = "user-name"
app_key = "access-key"
#Set capabilities for testing on Chrome
ch_caps = {
"build" : "Nose2 Testing using Chrome on Windows Environment(4)",
"name" : "Nose2 Testing on Chrome using Selenium Grid Environment(4)",
"platform" : "Windows 10",
"browserName" : "Chrome",
"version" : "71.0",
"selenium_version" : "3.13.0",
"chrome.driver" : 2.42
}
#Set capabilities for testing on Firefox
ff_caps = {
"build" : "Nose2 Testing using Firefox on Windows Environment(5)",
"name" : "Nose2 Testing on Firefox using Selenium Grid Environment(5)",
"platform" : "Windows 10",
"browserName" : "Firefox",
"version" : "64.0",
}
#Set capabilities for testing on Safari
saf_caps = {
"build" : "Nose2 Testing using Safari on macOS Mojave Environment(6)",
"name" : "Nose2 Testing on Safari using Selenium Grid Environment(6)",
"platform" : "macOS Mojave",
"browserName" : "Safari",
"version" : "12.0",
}
# class Layer(object):
# @classmethod
# def setUp(cls):
# urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# print("Test Mod 2 - Inside setUp")
# @classmethod
# def testTearDown(cls):
# global driver
# print("Test Mod 2 - Inside tearDown")
# driver.quit()
class Test_Parallel_test_2(unittest.TestCase):
# layer = Layer
def setUp(self):
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
print("Test Mod 2 - setUp initiated")
@params((ch_caps), (ff_caps), (saf_caps))
def test_lambdatest_google_search(self, caps):
# Details can be sourced from https://automation.lambdatest.com/
remote_url = "https://" + user_name + ":" + app_key + "@hub.lambdatest.com/wd/hub"
self.driver = webdriver.Remote(command_executor = remote_url, desired_capabilities = caps)
self.driver.get('https://www.google.com')
self.driver.maximize_window()
title = "Google"
assert title == self.driver.title
search_text = "LambdaTest"
search_box = self.driver.find_element(By.XPATH, "//input[@name='q']")
search_box.send_keys(search_text)
# Using Sleep is not a good programming practice
# Only used here for demonstration purpose
time.sleep(5)
search_box.submit()
time.sleep(5)
# Click on the LambdaTest HomePage Link
title = "Most Powerful Cross Browser Testing Tool Online | LambdaTest"
lt_link = self.driver.find_element(By.XPATH, "//h3[.='LambdaTest: Most Powerful Cross Browser Testing Tool Online']")
lt_link.click()
time.sleep(10)
assert title == self.driver.title
time.sleep(2)
def tearDown(self):
# Close the browser
print("Test Mod 2 - TearDown initiated")
self.driver.quit()
if __name__ == '__main__':
import nose2
nose2.main()
代码演练
在两个测试案例中,都使用了标准的setUp**/tearDown**固定装置。浏览器资源在tearDown 方法中被释放。
def setUp(self):
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
print("Test Mod 2 - setUp initiated")
............................
............................
def tearDown(self):
# Close the browser
print("Test Mod 2 - TearDown initiated")
self.driver.quit()
if __name__ == '__main__':
import nose2
nose2.main()
浏览器和操作系统的能力是使用LambdaTest能力生成器生成的,其样本如下所示。
#Set capabilities for testing on Safari
saf_caps = {
"build" : "Nose2 Testing using Safari on macOS Mojave Environment(6)",
"name" : "Nose2 Testing on Safari using Selenium Grid Environment(6)",
"platform" : "macOS Mojave",
"browserName" : "Safari",
"version" : "12.0",
}
这些能力以参数化的形式使用@params 装饰器传递。
@params((ch_caps), (ff_caps), (saf_caps))
def test_lambdatest_google_search(self, caps):
# Details can be sourced from https://automation.lambdatest.com/
remote_url = "https://" + user_name + ":" + app_key + "@hub.lambdatest.com/wd/hub"
self.driver = webdriver.Remote(command_executor = remote_url, desired_capabilities = caps)
实现的其余部分是不言自明的,因为Selenium WebDriver的API被用来实现最终结果。
执行
以下命令用于触发并行测试的执行。
nose2 --verbose Nose_LT_Parallel_Test_3 Nose_LT_Parallel_Test_3_1 --plugin=nose2.plugins.mp -N 4
使用–plugin 选项启用多进程插件(nose.plugins.mp)。要并行执行的进程数量是4 。我在LambdaTest上的计划最多支持五个进程的并行,因此,我们选择四个进程进行演示。
这是LambdaTest中Automation选项卡的截图,它表明四个测试被并行执行。
这是LambdaTest中测试完成的截图,以及测试执行被触发的终端。
六个跨浏览器测试的总执行时间为63.52秒。在Python Nose教程的这一部分,我们已经涵盖了用Nose2进行Selenium Python测试的基本方面。
Nose2中的日志和报告
Selenium中的高级错误报告有助于缩小测试人员和开发人员之间的差距。它们还有助于以更快的速度修复问题,从而提高网络产品的整体质量。在Python Nose教程的这一部分,我们看看如何在Nose2 Python中创建报告。
Nose2有内置插件,可以让你在测试执行过程中捕获日志。这些日志被附加到失败测试的Python Nose测试报告中。Nose2 Python中的日志功能可以从终端设置(当触发执行命令时),或者通过在unittest.cfg(或nose2.cfg)文件中添加配置。
[log-capture]
always-on = False
clear-handlers = False
filter = -nose
format = %(name)s: %(levelname)s: %(message)s
log-level = NOTSET
我更喜欢用–log-capture 命令行选项来启用Nose2的日志记录。这里是启用日志的命令。
nose2 --verbose --log-capture Nose_LT_Basic_Test_1
下面是本Python Nose教程中早先演示的第一个测试的日志输出。
nose2-html-report 插件是一个外部插件,用于生成基于 HTML 的 Python Nose 测试报告。nose2-html-report 的最新版本是 0.6.0。下面的命令用于安装该插件。
pip install nose2-html-report
使用该插件,可以生成HTML或XML格式的报告。如果在nose2.cfg (或unittest.cfg )的[unittest] 部分的[plugin] 键中添加条目,Nose2可以识别该插件。下面是为你的单元测试生成HTML报告的配置样本。
[unittest]
plugins = nose2_html_report.html_report
[test-result]
always-on = True
descriptions = True
[html-report]
always-on = True
path = Path_to_report_file\report.html
为了给同一个测试用例生成HTML Python Nose测试报告,我们在终端运行以下命令。
nose2 --verbose --log-capture --plugin=nose2_html_report.html_report Nose_LT_Basic_Test_1
下面是执行截图和生成的HTML Python Nose测试报告。
Nose2、Nose 和 unittest2 之间的区别
现在我们已经涵盖了与Nose2 Python有关的所有方面,让我们看看Nose2与Nose(以及Python中的其他自动化框架)有什么不同。下面是Nose2与其前身之间的一些关键区别:
- Nose2适用于Python团队目前支持的Python版本,而Nose只支持Python 2.4版本(及以上)。
- 像unittest2一样,Nose2只支持模块级和类级的固定程序,而不支持包级固定程序。
- 与遵循懒惰加载的Nose不同,Nose2不需要一个自定义的导入器,因为它用
__import__()来导入测试模块。 - 对参数化测试和生成器测试的支持比Nose更广泛。Nose2在测试类、测试函数和
unittest TestCase子类中支持测试生成器。 - 就加载插件而言,Nose使用
setuptools入口点来寻找和加载插件。另一方面,Nose2不将任何插件加载到测试系统中,并规定插件应在配置文件中列出。 - 在Nose2中,所有的配置都必须通过配置文件来完成。另一方面,Nose希望配置参数可以作为命令行选项。因此,如果你想进行重复的测试运行,Nose2是首选,因为配置被存储在更易读的配置(.cfg)文件中。
我们的Python Nose教程系列的第二部分就到此结束了!
总结
在Python Nose教程系列的第二部分,我们深入研究了Nose2 Python,并将其用于Selenium测试自动化。如果你正在使用unittest 框架进行Selenium Python测试,你应该看看Nose2,因为它通过其广泛的插件生态系统为unittest 框架增加了更多价值。
Nose2也适合跨浏览器测试,因为它通过多进程(mp)插件支持并行测试执行,并让你下载Python Nose测试报告。在本地Selenium Grid上工作的现有实现可以被无缝移植到基于云的Selenium Grid(如LambdaTest)上工作。这有助于利用Nose2的制胜功能来加速测试过程。