【问题标题】:Test method with a mock response, without create data具有模拟响应的测试方法,无需创建数据
【发布时间】:2021-09-17 04:57:33
【问题描述】:

我正在使用 unittest 测试一个方法 createData,它会在我的数据库中创建一些东西。

def createData(self, content):
    logging.info("Creating data...")
    request = requests.post(self.url, data=content)
    if request.status_code == 201:
        logging.info("Data created")
    else:
        logging.error("Data not created")
            
    return request

所以我创建了两个测试:一个是我创建数据失败的测试,self.assertNotEqual(201, badRequest.status_code),另一个是我成功的测试,self.assertEqual(201, goodRequest.status_code)。当然,之后,我会删除这些数据。

我想在不创建任何数据的情况下进行此测试。所以我像这样嘲笑回应:

import unittest, logging
from data import Data as data
from unittest.mock import Mock

class TestData(unittest.TestCase):

    def testCreateDataSuccess(self):
        mock_response = Mock()
        mock_response.status_code = 201
        with self.assertLogs() as captured:
            data.createData(data, goodContent).return_value = mock_response
            self.assertEqual(201, mock_response.status_code)
            self.assertEqual(captured.records[1].levelname, 'INFO')

但是,尽管进行了模拟,但还是在我的数据库中创建了一个数据。你能告诉我我不明白的地方吗?

感谢您的帮助!

【问题讨论】:

  • 你能发布整个测试吗? createData(badContent).return_value = mock_response 可能没有做你期望的事情
  • 可能是的,我是 Python 和测试的初学者 -_-'
  • 查看更多测试内容会很有帮助:包括在测试文件顶部的导入。测试文件多长时间?你能把整件事都贴出来吗?
  • 其实只是缩进错误,非常抱歉!但你是对的,mock 并没有像我想的那样做,因为再次创建了一个数据......

标签: python python-3.x unit-testing mocking python-unittest


【解决方案1】:

Mocks 通常使用mock.patch 应用。您要模拟的是来自requests.post 的响应,而不是来自createData 的响应。弄清楚在哪里模拟可能非常棘手。本指南可以提供帮助 (https://alexmarandon.com/articles/python_mock_gotchas/)

您可能需要重新设计导入被测代码的方式,以便能够在正确的位置进行模拟。

import unittest, logging
import data  # We are importing the entire module. This will let us patch in the correct location
from unittest.mock import Mock

class TestData(unittest.TestCase):

    def testCreateDataSuccess(self):
        mock_response = Mock()
        mock_response.status_code = 201
        # using mock.patch, we can replace the response from requests.post with our mock value. `requests` is imported in the `data` module so we mock `data.requests`
        with mock.patch(data.requests.post, return_value=mock_response):
            with self.assertLogs() as captured:
                data.Data.createData(data, goodContent)
                # self.assertEqual(201, mock_response.status_code) This line does not actually do anything - it's just testing that our mock has the status code we set. But we already know that, because we set it just a few lines ago. 
                self.assertEqual(captured.records[1].levelname, 'INFO')

【讨论】:

  • 我们在同一时间发帖 :) 谢谢你的解释!但是,由于我正在设置 status_code(你是对的,在几行之后测试它是愚蠢的......),如果没有任何 logging.info 如何测试结果?
  • 我很高兴你的补丁工作正常了!为了测试结果,您需要测试在 if 块中发生的任何事情。因此,如果您在 if 块中创建数据记录,您将测试数据记录是否已创建。如果您在 if block function, you can mock out that function as well and use assert_call_with` 中调用外部库来测试它是否被正确调用,而没有实际调用外部服务
【解决方案2】:

好吧,我找到了解决这个问题的方法:使用补丁装饰器。 我猜它“化解”了数据中的请求,用配置的模拟替换响应

import unittest, logging
from data import Data as data
from unittest.mock import patch

class TestData(unittest.TestCase):
    
    @patch('data.requests.post')
    def testCreateDataSuccess(self, mock_post):
        mock_post.return_value.status_code = 201
        with self.assertLogs() as captured:
            response = data.createData(data, goodContent)
            self.assertEqual(201, response.status_code)
            self.assertEqual(captured.records[1].levelname, 'INFO')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-04
    • 2015-08-07
    • 2018-04-07
    • 1970-01-01
    • 2020-05-19
    • 2016-05-26
    • 1970-01-01
    • 2012-10-13
    相关资源
    最近更新 更多