Python Pyramid 简明教程

Python Pyramid - Testing

编写测试脚本以确保代码正常工作被认为是一种良好的编程实践。Python 生态系统具有多个测试框架,包括包含在标准库中的 unittestPytest 是一个流行的测试库。它是 Pyramid 项目的首选库。

Writing test scripts which ensure that your code works correctly is considered as a good programming practice. Python ecosystem had a number of testing frameworks, including unittest which is bundled in the standard library. Pytest is a popular testing library. It is a preferred library for Pyramid projects.

我们将使用我们之前在展示 PasteDeploy 配置使用方法时开发的 hello 包。

We shall use the hello package that we developed earlier while demonstrating the use of PasteDeploy configuration.

首先,确保 Pyramid 环境安装了 PyTest 包。

First, ensure that the Pyramid environment has PyTest package installed.

pip3 install pytest

打开 hello 包中的 setup.py 文件并通过添加以粗体显示的行对其进行修改。

Open the setup.py file in hello package and modify it by adding the lines shown in bold.

from setuptools import setup

requires = [
   'pyramid',
   'waitress',
]
dev_requires = ['pytest',]
setup(
   name='hello',
   install_requires=requires,
   extras_require={
      'dev': dev_requires,
   },
   entry_points={
      'paste.app_factory': [
         'main = hello:main'
      ],
   },
)

此处,只要使用以下命令安装(或重新安装),就会将 Pytest 添加为项目依赖项 −

Here, Pytest is added as the project dependency whenever it is installed (or reinstalled) using following command −

pip3 install -e ".[dev]

将以下 Python 代码存储为 hello 包中的 testing.py。

Store the following Python code as testing.py in hello package.

import unittest
from pyramid import testing
class HelloTests(unittest.TestCase):
   def test_hello_world(self):
      from . import hello_world
      request = testing.DummyRequest()
      response = hello_world(request)
      self.assertEqual(response.status_code, 200)

要运行测试,请使用以下 Pytest 命令。测试的输出如下所示 −

To run the tests, use following Pytest command. The output of the test is shown below −

Env\hello>pytest tests.py
========================== test session starts ==========================
platform win32 -- Python 3.10.1, pytest-7.1.2, pluggy-1.0.0
rootdir: E:\tp-pyramid\hello
collected 1 item

tests.py.
   [100%]

=========================== 1 passed in 1.12s ===========================

要检查测试是否失败,请在测试函数中引发错误并重新运行。

To check if the test fails, induce an error in the test function and run again.

(tp-pyramid) E:\tp-pyramid\hello>pytest tests.py
========================== test session starts ==========================
collected 1 item

tests.py F
[100%]
=============================== FAILURES ================================
______________________ HelloTests.test_hello_world ______________________
self = <hello.tests.HelloTests testMethod=test_hello_world>
   def test_hello_world(self):
      from . import hello_world
      request = testing.DummyRequest()
      response = hello_world(request)
>     self.assertEqual(response.status_code, 404)
E     AssertionError: 200 != 404

tests.py:13: AssertionError
======================== short test summary info ========================
FAILED tests.py::HelloTests::test_hello_world - AssertionError: 200 != 404
=========================== 1 failed in 1.53s ===========================

Functional Testing

尽管单元测试在测试驱动开发 (TDD) 方法中广为使用,但对于 Web 应用程序, WebTest 是一个执行功能测试的 Python 包。我们可以对 WSGI 应用程序模拟完整的 HTTP 请求,然后测试响应中的信息。

Although Unit tests are popularly used in test-driven development (TDD)approach, for web applications, WebTest is a Python package that does functional testing. We can simulate a full HTTP request against a WSGI application, then test the information in the response.

Example

让我们使用我们在前面示例中使用的 hello 项目。打开 setup.py 并将 WebTest 添加为项目依赖项。

Let us use the hello project that we had used in the earlier example. Open the setup.py and add WebTest as the project dependency.

from setuptools import setup

requires = [
   'pyramid',
   'waitress',
]
dev_requires = ['pytest','webtest',]
setup(
   name='hello',
   install_requires=requires,
   extras_require={
      'dev': dev_requires,
   },
   entry_points={
      'paste.app_factory': [
         'main = hello:main'
      ],
   },
)

重新安装 hello 包及其新依赖项以进行开发模式。

Reinstall the hello package and its new dependency for development mode.

Env\hello>..\scripts\pip3 install -e ".[dev]"

tests.py 文件中包含一个功能测试

Include a functional test in tests.py file

import unittest
from pyramid import testing

class HelloTests(unittest.TestCase):

   def test_hello_world(self):
      from . import hello_world
      request = testing.DummyRequest()
      response = hello_world(request)
      self.assertEqual(response.status_code, 200)
class HelloFunctionalTests(unittest.TestCase):
   def setUp(self):
      from . import main
      app = main({})
      from webtest import TestApp
      self.testapp = TestApp(app)
   def test_hello_world(self):
      res = self.testapp.get('/', status=200)
      self.assertIn(b'<h1>Hello World!</h1>', res.body)

Output

最后,按照以下命令运行 Pytest −

Finally run Pytest as per the following command −

Env\hello>pytest tests.py
========================== test session starts ==========================
platform win32 -- Python 3.10.1, pytest-7.1.2, pluggy-1.0.0
rootdir: E:\tp-pyramid\hello
collected 2 items
tests.py .. [100%]

=========================== 2 passed in 2.37s ===========================

Tests in Cookiecutter Project

CookieCutter 实用程序自动生成包含功能测试和单元测试的测试包。我们之前曾使用 Cookiecutter 构建名为 testproj 的 Pyramid 项目。在此项目中,我们找到 tests 文件夹。

The CookieCutter utility auto-generates the tests package containing functional tests and unit tests. We had earlier used Cookiecutter to build Pyramid project named testproj. In this project, we find tests folder.

Example

test_functional py 包含以下测试函数 −

The test_functional py contains the following test functions −

from testproj import models

def test_my_view_success(testapp, dbsession):
   model = models.MyModel(name='one', value=55)
   dbsession.add(model)
   dbsession.flush()
   res = testapp.get('/', status=200)
   assert res.body

def test_notfound(testapp):
   res = testapp.get('/badurl', status=404)
   assert res.status_code == 404

test_views.py 定义了以下测试函数来测试视图 −

The test_views.py defines following test functions to test the views −

from testproj import models
from testproj.views.default import my_view
from testproj.views.notfound import notfound_view

def test_my_view_failure(app_request):
info = my_view(app_request)
assert info.status_int == 500

def test_my_view_success(app_request, dbsession):
   model = models.MyModel(name='one', value=55)
   dbsession.add(model)
   dbsession.flush()
   info = my_view(app_request)
   assert app_request.response.status_int == 200
   assert info['one'].name == 'one'
   assert info['project'] == 'testproj'
def test_notfound_view(app_request):
   info = notfound_view(app_request)
   assert app_request.response.status_int == 404
   assert info == {}

Output

这些测试由以下命令运行 −

These tests are run by the following command −

Env\testproj>Pytest
========================== test session starts ==========================
platform win32 -- Python 3.10.1, pytest-7.1.2, pluggy-1.0.0
rootdir: Env\testproj, configfile: pytest.ini, testpaths: testproj, tests
plugins: cov-3.0.0
collected 5 items

tests\test_functional.py .. [ 40%]
tests\test_views.py ... [100%]
=============== 5 passed, 20 warnings in 6.66s ===============