简体   繁体   English

如何编写 pytest 来测试在单个 function 中打开两个单独的文件

[英]How to write a pytest to test for opening two separate files in a single function

How do I use pytest to check that two different files in the same function are opened correctly?如何使用 pytest 检查同一 function 中的两个不同文件是否正确打开?

open_two_files.py open_two_files.py

def open_two_files(input_filepath, output_filepath):
    open_output = open(output_filepath, 'w')
    with open(input_filepath, 'r') as open_input:
      for line in open_input:
        open_output.write('foo')

I used this answer to build a test我用这个答案来建立一个测试

test_open_two_files.py test_open_two_files.py

import pytest
from unittest.mock import patch, mock_open
from open_two_files import *


def test_open_two_files():
    open_mock = mock_open()
    with patch('open_two_files.open', open_mock, create=True):
        open_two_files("input_filepath", "output_filepath")
    open_mock.assert_called_with("input_filepath", 'r')
    # pytest passes with the following line commented out
    open_mock.assert_called_with("output_filepath", 'w')

The error I get is我得到的错误是

E           AssertionError: expected call not found.
E           Expected: open('output_filepath', 'w')
E           Actual: open('input_filepath', 'r')

If I comment out the last line, the tests pass.如果我注释掉最后一行,则测试通过。 The testing seems to only look at the last time that a file is opened.测试似乎只查看文件最后一次打开的时间。 How do I test all occurrences of a file being opened please?请问如何测试所有出现的文件被打开?

If you check the documentation for assert_called_with , you will see:如果您查看assert_called_with文档,您将看到:

This method is a convenient way of asserting that the last call has been made in a particular way此方法是断言最后一次调用是以特定方式进行的便捷方式

(emphasis mine) (强调我的)

You can check instead for any call with these arguments by using assert_any_call :您可以使用assert_any_call来检查这些 arguments 的任何调用:

def test_open_two_files():
    open_mock = mock_open()
    with patch('open_two_files.open', open_mock, create=True):
        open_two_files("input_filepath", "output_filepath")
    assert open_mock.call_count == 2
    open_mock.assert_any_call("input_filepath", 'r')
    open_mock.assert_any_call("output_filepath", 'w')

If you also want to check the sequence of the calls, you have to check each call separately:如果您还想检查调用的顺序,则必须分别检查每个调用:

def test_open_two_files():
    ...
    assert open_mock.call_count == 2
    assert open_mock.call_args_list[0][0] == ("output_filepath", 'w')
    assert open_mock.call_args_list[1][0] == ("input_filepath", 'r')

or, if you are at least under Python 3.8:或者,如果您至少在 Python 3.8 下:

def test_open_two_files():
    ...
    assert open_mock.call_count == 2
    assert open_mock.call_args_list[0].args == ("output_filepath", 'w')
    assert open_mock.call_args_list[1].args == ("input_filepath", 'r')

(the args and kwargs attributes for the call args have been introduced in Python 3.8) (调用 args 的argskwargs属性已在 Python 3.8 中引入)

I created an open source pytest fixture to help me create the asserts automatically to save some time in situations like this.我创建了一个开源 pytest 夹具来帮助我自动创建断言以在这种情况下节省一些时间。

If you pip install pytest-mock-generator , you would get the mock generator fixture, which is called mg and can be used like so:如果您pip install pytest-mock-generator ,您将获得名为mg的模拟生成器夹具,可以像这样使用:

def test_open_two_files(mg):
    open_mock = mock_open()
    with patch('open_two_files.open', open_mock, create=True):
        open_two_files("input_filepath", "output_filepath")
    open_mock.assert_called_with("input_filepath", 'r')

    mg.generate_asserts(open_mock)

Then execute your test and the fixture would print to the console and also copy the following to your clipboard:然后执行您的测试,夹具将打印到控制台,并将以下内容复制到剪贴板:

from mock import call

assert 2 == open_mock.call_count
open_mock.assert_has_calls(calls=[call('output_filepath', 'w'),call('input_filepath', 'r'),])
open_mock.return_value.__enter__.assert_called_once_with()
open_mock.return_value.__iter__.assert_called_once_with()
open_mock.return_value.__exit__.assert_called_once_with(None, None, None)

You can insert the code as is to your test or modify it to your needs.您可以将代码按原样插入到测试中,也可以根据需要对其进行修改。 Say that you don't care about testing the enter and exit from the context, your final test would look like so:假设您不关心测试上下文的进入和退出,您的最终测试将如下所示:

from unittest.mock import patch, mock_open, call
from open_two_files import *


def test_open_two_files():
    open_mock = mock_open()
    with patch('open_two_files.open', open_mock, create=True):
        open_two_files("input_filepath", "output_filepath")
    open_mock.assert_called_with("input_filepath", 'r')

    assert 2 == open_mock.call_count
    open_mock.assert_has_calls(calls=[call('output_filepath', 'w'),call('input_filepath', 'r'),])

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

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