简体   繁体   English

我如何使用 pytest 测试 python 中的无效 function?

[英]How can i test a void function in python using pytest?

I just started unit testing in python using pytest. Well, when I have a function with a return value, with the "assert" I can compare a certain value with the value that the function return.我刚开始使用 pytest 在 python 中进行单元测试。好吧,当我有一个带有返回值的 function 时,通过“断言”我可以将某个值与 function 返回的值进行比较。 But if I had a void function that returns nothing and does a print at the end, for example:但是如果我有一个 void function 什么都不返回并在最后打印,例如:

def function() -> None:
    number = randint(0, 4)

    if (number == 0):
       print("Number 0")

    elif (number == 1):
       print("Number 1")

    elif (number == 2):
       print("Number 2")

    elif (number == 3):
       print("Number 3")

    elif (number == 4):
       print("Number 4")

How can i test this simple function to get 100% code coverage?我如何测试这个简单的 function 以获得 100% 的代码覆盖率?

One method I've found to test this function is to do a return of the value (instead of print) and print it later, and then use the assert.我发现测试这个 function 的一种方法是返回值(而不是打印)并稍后打印它,然后使用断言。 But I wanted to know if it was possible to avoid this and do a test directly on the print statemant.但我想知道是否有可能避免这种情况并直接在打印语句上进行测试。

You can redirect sys.stdout (the stream that print writes to) to a buffer and then examine or assert the contents of the buffer.您可以将sys.stdoutprint写入的 stream)重定向到缓冲区,然后检查或断言缓冲区的内容。

>>> import io
>>> import contextlib
>>> 
>>> def f():print('X')
... 
>>> buf = io.StringIO()
>>> with contextlib.redirect_stdout(buf):
...     f()
... 
>>> print(repr(buf.getvalue()))
'X\n'
>>> 
>>> buf.close()

(Recall that print() appends the value of its end argument to the line, which defaults to '\n' ). (回想一下print()将其end参数的值附加到该行,默认为'\n' )。

I suggest having a look at the plugin pytest-mock .我建议看看插件pytest-mock It allows you to mock collaborating objects of your code under test.它允许您模拟被测代码的协作对象。

Consider the following code under test:考虑以下被测代码:

# production.py 

def say_hello() -> None:
    print('Hello World.')

you can easily mock this now with你现在可以很容易地嘲笑这个

# production_test.py
from production import say_hello

def test_greeting(mocker):
    # The "mocker" fixture is auto-magicall inserted by pytest, 
    # once the extenson 'pytest-mock' is installed
    printer = mocker.patch('builtins.print')
    say_hello()
    assert printer.call_count == 1

You can also assert the arguments the printer function was called with, etc. You will find a lot of details in their useful documentation .您还可以断言 arguments 打印机 function 被调用等。您会在它们有用的文档中找到很多详细信息。


Now, consider you do not want to access the printer , but have a code with some undesirable side-effects (eg an operation takes forever, or the result is non-predictable (random).) Let's have another example, say现在,假设您不想访问printer ,但是有一个带有一些不良副作用的代码(例如,一个操作需要永远,或者结果是不可预测的(随机的)。)让我们再举一个例子,比如说

# deep_though.py

class DeepThought:
    #: Seven and a half million years in seconds
    SEVEN_HALF_MIO_YEARS = 2.366771e14

    @staticmethod
    def compute_answer() -> int:
        time.sleep(DeepThought.SEVEN_HALF_MIO_YEARS)
        return 42

yeah, I personally don't want my test suite to run 7.5 mio years.是的,我个人不希望我的测试套件运行 7.5 mio 年。 So, what do we do?那么我们该怎么办?

# deep_thought_test.py 
from deep_thought import DeepThought

def test_define_return_value(mocker) -> None:
    # We use the internal python lookup path to the method 
    # as an identifier (from the location it is called)      
    mocker.patch('deep_thought.DeepThought.compute_answer', return_value=12)
    assert DeepThought.compute_answer() == 12

Two more minor remarks, not directly related to the post:还有两点小意见,与帖子没有直接关系:

  • A high code coverage (80% - 90%) is a good goal.高代码覆盖率 (80% - 90%) 是一个不错的目标。 I personally try to stck around 90-95%.我个人尝试保持在 90-95% 左右。 However, 100% coverage is usually not necessary.但是,通常不需要 100% 的覆盖率。 Simple(.) data items and log-statements can usually be ignored. Simple(.) 数据项和日志语句通常可以忽略。
  • It's a good practice to use a logger, instead of print .使用记录器而不是print是一个好习惯。 See Question 6918493 for example.例如参见问题 6918493

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM