python基础知识测试题
Unit testing is the number one skill which separates people who just finished their degrees from people with practical experience. Especially for Python, that’s a shame as it is trivial to learn this skill.
单元测试是将刚完成学业的人与有实践经验的人区分开来的第一技能。 尤其是对于Python来说,这太可惜了,因为学习这项技能很简单。
In this article, you will learn how to write and run unit tests in Python as well as some interesting pytest plugins I usually use. Let’s get started.
在本文中,您将学习如何使用Python以及我通常使用的一些有趣的pytest插件编写和运行单元测试。 让我们开始吧。
最基本的单元测试 (The most basic Unit Test)
A unit test is atomic- it just tests one unit of code. Typically one function or one method of a class. As an example, let’s say we want to test math_functions.py
which contains the Fibonacci function and a function for the Collatz sequence:
单元测试是原子的-它仅测试一个单元代码。 通常是一个函数或类的一种方法。 例如,假设我们要测试math_functions.py
,其中包含Fibonacci函数和Collatz序列的函数:
We want to test this function. I will explain the reasons for testing and what testing means later. For now, let’s just say we want to avoid programming errors.
我们要测试此功能。 稍后我将解释测试的原因以及测试的意义。 现在,我们只想避免编程错误。
First, create a file test_math_functions.py
:
首先,创建一个文件test_math_functions.py
:
Now, you have to install pytest:
现在,您必须安装pytest:
$ pip install pytest
And run it:
并运行它:
$ pytest
============ test session starts ===================================
platform linux -- Python 3.8.1, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /home/moose/GitHub/MartinThoma/algorithms/medium/unit-testing
collected 5 itemstest_math_functions.py ..... [100%]============ 5 passed in 0.03s =====================================
Awesome! You can see that it took 0.03 seconds to execute. There are 5 dots after the test_math_functions.py
. Those indicate that 5 tests were executed and successful.
太棒了! 您可以看到它执行了0.03秒。 在test_math_functions.py
之后有5个点。 这些表明已经执行了5个测试,并且成功。
Let’s break one test, e.g. test_fib_3
by setting assert fib(3) == 1337
. Then you see this:
让我们通过设置assert fib(3) == 1337
破坏一个测试,例如test_fib_3
。 然后,您会看到以下内容:
============ test session starts =================================
platform linux -- Python 3.8.1, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /home/moose/GitHub/MartinThoma/algorithms/medium/unit-testing
collected 5 itemstest_math_functions.py ..F.. [100%]================== FAILURES =======================================
_________________ test_fib_3 ______________________________________def test_fib_3():
> assert fib(3) == 3
E assert 2 == 3
E + where 2 = fib(3)test_math_functions.py:14: AssertionError
============ short test summary info ===============================
FAILED test_math_functions.py::test_fib_3 - assert 2 == 3
============ 1 failed, 4 passed in 0.03s ===========================
Great. Now you know how to write a unit test.
大。 现在您知道如何编写单元测试了。
词汇 (Vocabulary)
The units we are testing in the section above are functions — fib
and next_collatz_element
.
我们在上一节中测试的单元是函数— fib
和next_collatz_element
。
We have 5 unit tests; all of them in test_math_functions.py
: The test_*
functions.
我们有5个单元测试 ; 他们都在 test_math_functions.py
test_*
函数。
The pytest
command-line executable is called a test runner. It executes (runs) the tests.
pytest
命令行可执行文件称为“ 测试运行器” 。 它执行(运行)测试。
A test suite is an arbitrary collection of tests. Usually, you mean all tests.
测试套件是测试的任意集合。 通常,您指的是所有测试。
我们为什么要测试? (Why do we test at all?)
Trust: You checked at least some cases if they work. So others can have more trust in the quality of your work and you can also put more trust in it.
信任 :您至少检查了某些情况是否有效。 因此,其他人可以对您的工作质量有更多的信任,并且您也可以对它有更多的信任。
Breaking Changes: For a bigger project, it is sometimes hard to have every part in mind. By writing tests, you make it easier to change something and see if / where things break. This does not only help you but also team members. Including once that are not there yet.
重大变化 :对于更大的项目,有时很难牢记每个部分。 通过编写测试,您可以更轻松地进行更改并查看是否/在何处中断。 这不仅对您有帮助,对团队成员也有帮助。 包括曾经不存在的一次。
Code Style: When you know that you have to write tests, you write some things slightly differently. Those slight differences usually improve the coding style. Sometimes, they are crucial. For example, if you have to thoroughly test your code you will make smaller chunks.
代码样式 :当您知道必须编写测试时,您编写的某些内容会稍有不同。 这些细微的差异通常会改善编码风格。 有时,它们至关重要。 例如,如果您必须彻底测试代码,则将制作较小的块。
Documentation: Some test cases show a little bit of how the code is intended to be used.
文档 :一些测试用例显示了如何使用该代码。
测试范围 (Test Coverage)
I hope at this point we agree that having tests is a good idea. But how many tests do you need? When did you test everything?
我希望在这一点上我们同意测试是一个好主意。 但是您需要多少测试? 您什么时候测试了所有内容?
A group of measures for this is the test coverage. There are two relevant types of test coverage: Line coverage and branch coverage.
对此的一组措施是测试覆盖率 。 有两种相关的测试覆盖率类型:线路覆盖率和分支覆盖率。
If you look at the Collatz function from above, there are 4 lines to test:
如果从上方看Collatz函数,则有4行要测试:
If I execute next_collatz_element(4)
, then it will execute lines 1–3. Line 4 and 5 will not be hit. This means a unit test like that could not detect an issue on line 4 or 5. It only covers 3 of 5 lines. One says that it has 60% line coverage.
如果执行next_collatz_element(4)
,它将执行第1至3行。 第4行和第5行不会被选中。 这意味着像这样的单元测试无法在第4或5行上检测到问题。它仅涵盖5行中的3行。 有人说它有60%的线路覆盖率。
But sometimes 100% line coverage is not enough. Take a look at this example:
但是有时100%的线路覆盖率还不够。 看一下这个例子:
If you test greet("Angela", "Merkel")
you will have 100% line coverage. But you miss that if the last name is not given, the return value is None
. In the given test, the if-statement in line 2 always evaluates to “True”. You don’t cover a branch in the execution graph. So you have only 50% branch coverage.
如果您测试greet("Angela", "Merkel")
您将获得100%的线路覆盖率。 但是您错过了如果没有提供姓氏,则返回值为None
。 在给定的测试中,第2行的if语句始终评估为“ True”。 您不会在执行图中覆盖分支。 因此,您只有50%的分支机构覆盖率。
pytest-cov
is a pytest plugin to measure branch coverage.
pytest-cov
是用于测试分支覆盖率的pytest插件。
Install it with
pip install pytest-cover
用
pip install pytest-cover
Use it by adding
--cov=path/to/file
or--cov=packagename
to the pytest execution通过在pytest执行中添加
--cov=path/to/file
或--cov=packagename
来使用它Get output to terminal by adding to pytest
--cov-report term
通过添加到pytest
--cov-report term
获取输出到终端Get HTML output by adding
--cov-report html:tests/reports/coverage
通过添加
--cov-report html:tests/reports/coverage
获得HTML输出
There are more reporting capabilities.
有更多的报告功能 。
好的测试 (Good Tests)
It’s pretty hard to write good tests and when you measure your test coverage it is tempting to quickly write a couple of bad tests.
编写好的测试非常困难,当您测量测试覆盖率时,很容易快速编写几个不好的测试。
Worst is no testing at all.
最糟糕的是根本没有测试。
A little bit better is a test that just executes a function but does not check if the return value/the side effects are what you expect. So you simply run it to check if the code crashes.
更好一点的测试是仅执行一个函数,而不检查返回值/副作用是否符合您的期望。 因此,您只需运行它来检查代码是否崩溃。
Happy-Tests where you check the output of the tested function and a typical input is even better. I call them happy because they test what you expect to get.
在Happy-Tests中,您可以检查已测试功能的输出以及典型的输入,效果更好。 我称他们为快乐,因为他们测试了您的期望。
In contrast, an unhappy execution path is dealing with unwanted inputs. This is also called negative testing. You check if you actually throw an error. Not throwing an error and silently failing is bad as it hides bugs.
相反, 不满意的执行路径正在处理不需要的输入。 这也称为负面测试 。 您检查是否确实抛出了错误。 不抛出错误并且静默失败是不好的,因为它隐藏了错误。
Property testing is pretty cool. There you don’t test for single values, but you check if a property is still held. For example, the output of a factorization function can be multiplied and should equal the input.
属性测试非常酷。 在那里,您无需测试单个值,但可以检查属性是否仍然保留。 例如,分解函数的输出可以相乘,并且应该等于输入。
类型检查 (Type Checking)
If you use type annotations (which you totally should!), then you can install pytest-mpy
. You can then automatically run mypy over your code by adding --mypy
to your pytest command.
如果使用类型注释 (完全应该使用!),则可以安装pytest-mpy
。 然后,您可以通过在您的pytest命令中添加--mypy
来自动在代码上运行mypy。
林亭 (Linting)
Code linting is the act of finding bugs, stylistic errors, and suspicious constructs from static code analysis.
代码整理是从静态代码分析中发现错误,样式错误和可疑结构的行为。
There are two linters I can recommend: black and flake8. You can run them with pytest by installing pytest-black
and pytest-flake8
. Again, if you want to execute it just add the flag --black
or --flake8
to pytest:
我可以推荐两种短毛绒 : black和flake8 。 您可以通过安装pytest-black
和pytest-flake8
与pytest一起运行它们。 同样,如果要执行它,只需在--black
添加--black
或--flake8
标志即可:
$ pytest --flake8 --black
There is also pytest-mccabe
which tries to find sections in the cod which are to complex. This makes it easier for coworkers / your future self to understand the code. An alternative to pytest-mccabe outside of pytest is radon
. However, a lot of people don’t like this type of test.
也有pytest-mccabe
试图在鳕鱼中查找复杂的部分。 这使同事/您将来的自我更容易理解代码。 pytest之外的pytest-mccabe的替代方法是radon
。 但是,很多人不喜欢这种类型的测试。
文档测试 (Doctests)
Doctests are a weird but pretty awesome part of Python. Python has Docstrings — the first string within a function/class which comes directly after the signature and which is not assigned. This is not just a comment, it has meaning and can be read through the execution:
Doctests是Python的一个怪异但非常棒的部分。 Python具有Docstrings -函数/类中的第一个字符串,紧接在签名之后且未分配。 这不仅是注释,它还具有含义并且可以通过执行来读取:
If you execute this directly, the lines 19–21 will run the doctest. The doctest looks for >>>
within the docstrings and executes whatever follows as if it was entered in the interactive console. The next line is then the output which is compared to the output of the program.
如果直接执行此操作,则第19–21行将运行doctest。 doctest在文档字符串中查找>>>
,然后执行以下操作,就像在交互式控制台中输入的一样。 然后,下一行是输出,将其与程序的输出进行比较。
This is pretty awesome because it makes documentation testable!
这非常棒,因为它使文档可测试!
And, of course, you can also execute doctests with pytest:
当然,您也可以使用pytest执行doctest :
$ pytest --doctest-modules
测试执行速度 (Test Execution Speed)
It’s important to keep the execution time of the tests low so that it doesn’t feel bad to execute the test suite. I like to print the time of the 3 slowest tests which were performed. To profile the tests continuously, I simply add the durations flag:
保持测试执行时间很短很重要,这样执行测试套件就不会感到不好。 我喜欢打印执行的3个最慢测试的时间。 为了连续描述测试 ,我只需添加durations标志:
$ pytest --durations=3
替代方案:单元测试和鼻子 (Alternatives: unittest and nose)
unittest is a core Python module and as such, I would prefer to use it. unittest feels pretty similar to JUnit which I would say is a disadvantage. Python is a different language with different patterns and expectations. One weirdness is that you have to put your tests in a class, even if you don’t need to setUp() or tearDown() anything. It uses camelCase for the method names which is against the Python conventions. You cannot simply assert Expression
, but instead, have to use self.assertEqual
, self.assertTrue
, … (see the complete list of assert methods). And the error messages are not as expressive as the ones you get from Pytest.
unittest是Python的核心模块,因此,我更愿意使用它。 unittest感觉与JUnit非常相似,我认为这是一个缺点。 Python是另一种语言,具有不同的模式和期望。 一个怪异的事情是,即使您不需要setUp()或tearDown()任何东西,也必须将测试放在一个类中。 它使用camelCase作为违反Python约定的方法名称。 您不能简单地assert Expression
,而必须使用self.assertEqual
, self.assertTrue
,…(请参阅assert方法的完整列表 )。 错误消息的表现力不及Pytest所提供。
TL;DR: unittest and nose are no alternatives. pytest is the way to go.
TL; DR:单元测试和鼻子是不可替代的。 pytest是要走的路。
下一步是什么? (What’s next?)
In this series, we already had:
在本系列中,我们已经有:
Part 1: The basics of Unit Testing in Python
第1部分: Python单元测试的基础
Part 2: Patching, Mocks and Dependency Injection
第2部分: 修补,模拟和依赖注入
Part 3: How to test Flask applications with Databases, Templates and Protected Pages
第3部分: 如何使用数据库,模板和受保护的页面测试Flask应用程序
Part 4: tox and nox
第4部分: 有毒和无毒
Part 5: Structuring Unit Tests
第5部分: 结构单元测试
Part 6: CI-Pipelines
第6部分: CI管道
Part 7: Property-based Testing
第7部分: 基于属性的测试
Part 8: Mutation Testing
第8部分: 变异测试
In future articles, I will present:
在以后的文章中,我将介绍:
- Static Code Analysis: Linters, Type Checking, and Code Complexity 静态代码分析:Linter,类型检查和代码复杂度
Let me know if you’re interested in other topics around testing with Python.
让我知道您是否对使用Python测试有关的其他主题感兴趣。
翻译自: https://medium.com/swlh/unit-testing-in-python-basics-21a9a57418a0
python基础知识测试题