Nose2 是 Python 中流行的测试运行器,可以检测项目中的单元测试并执行它们。如果您熟悉unittestPython 的标准库并且更喜欢 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 的 plugins 分支。
与 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
从下面的安装快照中可以看出,安装的是 0.9.2 版本的 Nose2。
在实现中使用import nose2 可以导入nose2 包。如果要使用包中的特定模块,可以使用以下命令导入相同的模块:
from nose2. import
如何执行 Nose2 Python 测试
由于 Nose2 与 Nose 不同,Nose2 中触发测试的命令也不同。这是在 Nose2 中执行测试的命令:
nose2 --verbose
例如,如果包含 Nose2 测试的文件名是Test_Nose2_example.py,则用于执行内部测试的命令应该是:
nose2 --verbose Test_Nose2_example
Nose2 测试运行器提供了许多用于捕获日志、报告等的选项,可以通过在执行测试时在控制台上传递它们来使用:
Nose2 中的测试发现
模块(或文件)和以 开头的测试用例遵循的命名法test_适用于 Nose2。包含测试方法的测试类应该以Test.
Nose2 提供了一个插件来实现自动测试模块发现。该插件在名称以 test 开头的包和目录中查找模块(或测试文件)。然后它触发loadTestsFromModule()所有发现的钩子,允许其他插件加载实际测试。您可以在 Nose2 中找到有关基于发现的测试加载器的更多详细信息。
Nose2 框架的示例用法
为了演示本 Python Nose 教程中 Nose2 框架的使用,我们使用前面演示 Nose 时使用的相同示例,即 Google 搜索 LambdaTest,然后单击第一个测试结果。
import unittestfrom selenium import webdriverimport timefrom time import sleepfrom selenium.webdriver.common.by import Byclass 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.titlesearch_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 purposetime.sleep(5)search_box.submit()time.sleep(5)# Click on the LambdaTest HomePage Linktitle = "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.titletime.sleep(2)# Release the resources in the teardown functionprint("TearDown initiated")driver.quit()if __name__ == '__main__':import nose2nose2.main()
unittest该实现与我们在使用该模块时可能使用的实现几乎相同。主要(但可选)包含是nose2.main(). 它的使用方式与目的相同unittest.main(),目的是在单个模块中运行测试。
if __name__ == '__main__':import nose2nose2.main()
Nose2 Python 中的参数化测试
与需要为测试参数化安装单独的包(即,nose-parameterized)的 Nose 不同,Nose2 Python 支持比 Nose 更多种类的参数化和生成器测试。它支持测试类、测试函数和unittestTestCase子类中的测试生成器。
该插件实现了以下功能,以支持从参数化测试函数和方法加载测试:
- getTestCaseNames()
- loadTestsFromModule()
- loadTestsFromName()
对于函数或测试用例方法的参数化,nose2.tools.params()使用。我们将在这个 Python Nose 教程中使用相同的方法。
nose2.tools.params(*paramList)
列表中的参数可以是简单的值或元组。例如,在本地 Selenium Grid 上执行 Selenium Python 测试(使用参数化)时,我们使用简单的值。另一方面,在基于云的 Selenium Grid(如 LambdaTest)上执行 Selenium 测试自动化时,我们将使用元组(即浏览器名称、浏览器版本和操作系统的组合)进行测试参数化。
此处提供有关用于参数化测试的 Nose2 Python 插件的更多详细信息。
我们移植了 Selenium Python Nose 教程中使用的示例,其中 LambdaTest ToDo 应用程序针对三种不同的浏览器进行了测试:Firefox、Microsoft Edge 和 Chrome。
- 导航到LambdaTest 示例应用程序 URL。
- 选中前两个复选框。
- 将“在 LambdaTest 进行快乐测试”发送到 id = sampletodotext 的文本框。
- 单击添加按钮并验证是否已添加文本。
执行
import unittestfrom selenium import webdriverimport timefrom time import sleepfrom selenium.webdriver.common.by import By# module for importing params functionalityfrom nose2.tools import paramsclass 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 accordinglyself.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.titlesample_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_textdef tearDown(self):# Close the browser.self.driver.quit()if __name__ == '__main__':import nose2nose2.main()
代码演练
由于测试方法必须参数化,我们将nose2.tools包中的 params 函数导入到当前文件中。
from nose2.tools import params
@params必须测试 ToDo 应用程序的浏览器作为装饰器下的参数传递。由于测试是在本地 Selenium Grid 上执行的,因此浏览器名称作为值传递。
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 accordinglyself.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 来定位所需的 Web 元素并对这些元素执行操作。由于这部分实现独立于测试框架,我们不会在本 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 unittestfrom selenium import webdriverimport timefrom time import sleepfrom selenium.webdriver.common.by import Byfrom nose2.tools import paramsclass 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.titlesearch_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 purposetime.sleep(5)search_box.submit()time.sleep(5)# Click on the LambdaTest HomePage Linktitle = "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.titletime.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.titlesample_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_textdef tearDown(self):# Close the browserprint("TearDown initiated")self.driver.quit()if __name__ == '__main__':import nose2nose2.main()
setup 的fixture(即,setUp())在类中的任何测试方法(包含fixture)被执行之前被调用。另一方面,用于拆卸的夹具(即tearDown())在每个测试方法(在类下实现)执行后被调用。
这里显示的是执行屏幕截图,显示setUp/tearDown被每个测试用例调用。
将测试夹具组织成层
在 Python Nose 教程的这一部分中,我们将了解图层,这是 Nose2(版本 0.4)中新引入的概念。与传统夹具相比,使用层的主要优点是夹具组织的灵活性。目的是与 Zope 使用的层兼容testrunner。
以下是使用层可以实现的一些事情:
- 通过在包中的所有测试用例之间共享一个层来实现包级夹具
- 创建比可用级别(测试、类和模块)更深的夹具树
- 在不同模块中的测试之间共享固定装置,而无需多次运行
这是一个至少实现一个setUp方法的类的简单演示:
class Layer(object):@classmethoddef 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.layers 。
unittest.cfg(或nose.cfg)
[unittest]plugins = nose2.plugins.layers[test-result]always-on = Truedescriptions = True[layer-reporter]always-on = Truecolors = True
Nose2 中的夹具(带图层)演示
对于本 Python Nose 教程中的夹具演示,我们使用基于云的 Selenium Grid,因为与本地 Selenium Grid 相比,它是一个更具可扩展性的选项。LambdaTest 就是这样一种基于云的 Selenium Grid,它提供了在 2,000 多种不同的浏览器、设备模拟器和操作系统组合上运行 Selenium 自动化测试的设施。
将在本地 Selenium Grid 上工作的现有实现移植到像 LambdaTest 这样的基于云的 Selenium Grid 并不涉及学习曲线。从投资和可扩展性的角度来看,在基于云的 Selenium Grid 上使用 Nose2 Python 进行 Selenium Python 测试是一个更好的选择。
在 LambdaTest 上创建帐户后,您应该记下 Profile 部分中用于访问 LambdaTest 上的 Selenium Grid 的用户名和访问密钥。您可以访问仪表板部分以获取有关现有测试和在平台上执行的早期测试的详细信息。
前面演示的 LambdaTest 上的 ToDo 应用程序由三种不同的浏览器和操作系统组合执行:
- Windows 10 上的 Chrome 71.0
- Windows 10 上的 Firefox 64.0
- macOS Mojave 上的 Safari 12.0
LambdaTest 上的功能生成器用于生成所需的浏览器和平台功能。下面显示的是为 Chrome 71.0 和 Windows 10 组合生成的浏览器功能快照。
执行
import unittestfrom selenium import webdriverimport timefrom time import sleepimport urllib3import warningsfrom selenium.webdriver.common.by import By# module for importing params functionalityfrom nose2.tools import paramsuser_name = "registered_email_address"app_key = "pass_key"#Set capabilities for testing on Chromech_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 Firefoxff_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 Safarisaf_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):@classmethoddef setUp(cls):urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)print("Inside setUp")@classmethoddef testTearDown(cls):global driverprint("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.driverself.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.titlesample_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 nose2nose2.main()
代码演练
1. 必要的 Nose2 Python 包,例如 ,nose2.tools在实现开始时被导入。
from nose2.tools import params
2. LambdaTest 的配置文件部分中可用的用户名和密码组合用于访问 LambdaTest Grid URL。
remote_url = "https://" + user_name + ":" + app_key + "@hub.lambdatest.com/wd/hub"
3. Selenium WebDriver API 使用使用在线生成器生成的 URL 以及浏览器和平台功能。
driver = webdriver.Remote(command_executor = remote_url, desired_capabilities = capabilities)
4. 最重要的部分是实现方法所在的Layer类setUp/testTearDown的实现。
class Layer(object):@classmethoddef setUp(cls):urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)print("Inside setUp")@classmethoddef testTearDown(cls):global driverprint("Inside tearDown")# Close the browser.driver.quit()
该方法setUp()将在测试方法类的开头调用。另一方面,testTearDown()将在作为类的一部分的每个测试方法结束时调用。清理活动(与被测浏览器相关)在该方法中执行,并且必须在每个测试方法之后执行。
而不是testTearDown,如果我们使用该tearDown方法,它只会被调用一次(即,在测试方法类的末尾)。我们使用testTearDown了因为浏览器资源需要在每个测试用例之后被释放。/方法包含在装饰器setUp下;否则,它们不会在测试执行期间被调用。testTearDown@classmethod
5.层需要分配给测试用例。因此,测试用例的 layer 属性使用如下所示的实现来设置:
class LT_Fixture_Test(unittest.TestCase):layer = Layer
@params6.当 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 来定位和执行这些 Web 元素上的操作,例如单击等。
执行
以下命令用于执行:
nose2 --verbose Nose_LT_Fixtures_Test_4
这里,Nose_LT_Fixtures_Test_4.py是包含测试实现的文件。下面显示的是LambdaTest 平台上自动化选项卡的屏幕截图。
Here is the screenshot that indicates the completion of the tests. All the test cases were executed serially, i.e., one after the other.
终端指示该setUp方法只运行一次,而该testTearDown方法是在完成每个测试方法后执行的。
Nose2 中的并行测试
Selenium 中的并行测试允许您在不同的测试环境中同时运行相同的测试。在 Python Nose 教程的这一部分,我们看看如何在 Nose2 Python 中实现并行测试。
该mp插件是在 Nose2 0.3 版中引入的,用于实现跨多个进程的测试分布。尽管并行测试执行会提高执行速度(因为测试是并行运行的),但这种改进在 IO-bound 测试中比 CPU-bound 测试更明显。您还需要检查 mp 插件的使用是否与其他不与它一起工作的插件不冲突。
以下是在 Nose2 Python 中实现并行性的一些方法:
- 通过在配置文件mp的部分中包含相同的插件来激活插件。[unittest]
[unittest]plugins = nose2.plugins.mp
- 或者,您可以通过使用–plugin命令行选项传递模块来做到这一点。
nose2 --plugin=nose2.plugin.mp
激活mp插件后,您必须配置可以并行运行的进程数。这可以通过以下-N选项来实现:
nose2 -N
这也可以通过在[multiprocess]配置文件的部分中设置进程数来实现:
[multiprocess]processes =
请务必注意,启用并行性时测试不会以相同的顺序运行。如果测试套件具有相互依赖关系(这本身不是一个好的编程习惯),那么如果mp启用了插件,测试可能会随机失败。您可以在标题为与 Nose2 并行运行测试的官方文档中找到针对测试作者的其他基本指南。
为了演示 Nose2 Python 中的并行测试执行,我们在这个 Python Nose 教程中跨六个不同的浏览器和操作系统组合执行两个测试场景。
测试用例 1
- 导航到LambdaTest 示例应用程序 URL。
- 选中前两个复选框。
- 将“在 LambdaTest 进行快乐测试”发送到 id = sampletodotext 的文本框。
- 单击添加按钮并验证是否已添加文本。
测试用例 2
- 导航到 Google 主页。
- 搜索“Lambdatest”。
- 找到第一个搜索结果并单击它。
- 如果打开的窗口的标题与预期的标题不匹配,则断言。
这两个测试场景都是针对以下浏览器和操作系统组合执行的:
- 铬 (71.0)、Windows 10
- 火狐 (64.0)、Windows 10
- Safari (12.0)、macOS 莫哈韦
对于并行 Selenium Python 测试,我们将使用旧式固定装置而不是层,因为使用层的测试套件与mp本 Python Nose 教程中的多进程(或)插件不兼容。不要尝试层和多进程的组合,因为它不起作用。
实施(测试用例 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-testingimport unittestfrom selenium import webdriverimport timefrom time import sleepimport urllib3import warningsfrom selenium.webdriver.common.by import By# module for importing params functionalityfrom nose2.tools import paramsuser_name = "user-name"app_key = "access-key"#Set capabilities for testing on Chromech_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 Firefoxff_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 Safarisaf_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 = Layerdef 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.titlesample_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_textdef tearDown(self):# Close the browserprint("Test Mod 1 - TearDown initiated")self.driver.quit()if __name__ == '__main__':import nose2nose2.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-testingimport unittestfrom selenium import webdriverimport timefrom time import sleepimport urllib3import warningsfrom selenium.webdriver.common.by import By# module for importing params functionalityfrom nose2.tools import paramsuser_name = "user-name"app_key = "access-key"#Set capabilities for testing on Chromech_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 Firefoxff_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 Safarisaf_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 = Layerdef 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.titlesearch_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 purposetime.sleep(5)search_box.submit()time.sleep(5)# Click on the LambdaTest HomePage Linktitle = "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.titletime.sleep(2)def tearDown(self):# Close the browserprint("Test Mod 2 - TearDown initiated")self.driver.quit()if __name__ == '__main__':import nose2nose2.main()
Code Walkthrough
In both the test cases, standard setUp/tearDownfixtures are used. Browser resources are freed in the tearDown method.
def setUp(self):urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)print("Test Mod 2 - setUp initiated")........................................................def tearDown(self):# Close the browserprint("Test Mod 2 - TearDown initiated")self.driver.quit()if __name__ == '__main__':import nose2nose2.main()
浏览器和操作系统功能是使用 LambdaTest 功能生成器生成的,其示例如下所示:
#Set capabilities for testing on Safarisaf_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
使用该选项nose.plugins.mp启用多进程插件 ( )。–plugin并行执行的进程数为4。因此,我对 LambdaTest 的计划支持最多五个进程的并行性;我们选择了四个进行演示。
这是 LambdaTest 中自动化选项卡的屏幕截图,它表明四个测试是并行执行的:
这是来自 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 = Falseclear-handlers = Falsefilter = -noseformat = %(name)s: %(levelname)s: %(message)slog-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 格式的报告。如果在( 或)部分的[plugin]键中添加条目,则 Nose2 可以识别该插件。以下是为您的单元测试生成 HTML 报告的示例配置:[unittest]nose2.cfg unittest.cfg
[unittest]plugins = nose2_html_report.html_report[test-result]always-on = Truedescriptions = True[html-report]always-on = Truepath = 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 测试自动化。如果您正在使用unittestSelenium Python 测试框架,您应该查看 Nose2,因为它unittest通过其广泛的插件生态系统为框架增加了更多价值。
Nose2 也适合跨浏览器测试,因为它支持通过多进程 ( mp) 插件执行并行测试,并允许您下载 Python Nose 测试报告。在本地 Selenium Grid 上工作的现有实现可以无缝移植以与基于云的 Selenium Grid(例如 LambdaTest)一起工作。这有助于利用 Nose2 的获胜功能来加快测试过程。
祝测试愉快!
热门跟贴