python中的单元测试_如果不测试代码,则在python中进行单元测试没有人会

python中的单元测试

Catch bugs before you even write them

甚至在编写错误之前就将其捕获

Python is a multi-purpose language that is used for everything backend. In this article, I will teach you to perform basic unit testing in Python, how to mock modules, and make sure your code is clean.

Python是一种用于所有后端的多用途语言。 在本文中,我将教您使用Python执行基本的单元测试,如何模拟模块并确保代码干净。

什么是单元测试? (What is Unit-testing?)

Unit-testing is one of the ways to test your code. Other ways include functional testing, integration testing, regression testing and so on. Testing is vital to any larger codebase, as it lets you iterate and perform changes quickly, without worrying to much about what is going to break.

单元测试是测试代码的方法之一。 其他方式包括功能测试,集成测试,回归测试等等。 测试对于任何更大的代码库都是至关重要的,因为它使您可以快速迭代并执行更改,而不必担心会发生什么。

Unit-testing is the lowest testing method on the abstraction level. Unit-testing is concerned with testing individual modules and functions in isolation. That is, by making sure all the parts of your system work correctly, you can make the assumption that the whole system is working ok.

单元测试是抽象级别上最低的测试方法。 单元测试是关于隔离测试单独的模块和功能。 也就是说,通过确保系统的所有部分正常工作,您可以假设整个系统都可以正常工作。

That is, in ideal world, of course. While unit-testing is very valuable, the whole system is not merely a sum of its parts: even if every function is working as intended, you will still need to test how well do they fit together (which is out of scope of this article).

就是说,在理想世界中。 尽管单元测试非常有价值,但是整个系统不仅仅是其各个部分的总和:即使每个功能都按预期工作,您仍然需要测试它们之间的配合程度(这超出了本文的范围) )。

单元测试与TDD有何关系? (How does Unit-testing relate to TDD?)

Unit-testing is one of the foundations of TDD. TDD stands for Test Driven Development, and is a methodology for producing quality software. In essence, it all boils down to these:

单元测试是TDD的基础之一。 TDD代表“测试驱动开发”,是一种生产高质量软件的方法。 从本质上讲,这全都归结为以下几点:

  1. Write tests first. Think about how different parts of your system work in isolation and write tests that validate their intended behaviour. Your tests must fail, because you have not written any actual code yet.

    首先编写测试。 考虑一下系统的不同部分如何隔离工作,并编写验证其预期行为的测试。 测试必须失败,因为您尚未编写任何实际代码。

  2. Write just enough code to make the tests pass.

    编写足够的代码以使测试通过。
  3. Refactor what you just wrote.

    重构您刚刚写的内容。
  4. Go to step 1.

    转到步骤1。

That is it! The whole cycle takes a couple of minutes, but this simple technique will make sure your system is (1) working according to the specs and (2) is testable. But to get started with TDD, you need to master basic unit-testing first.

这就对了! 整个周期需要花费几分钟,但是这种简单的技术将确保您的系统(1)根据规范工作,并且(2)是可测试的。 但是要开始使用TDD,您需要首先掌握基本的单元测试。

unittest模块 (unittest module)

Unit-testing in Python is available out of the box with the unittest module. We will learn unit-testing by developing our own factorial function. To get started, open a new Python file add write this code it it:

Python中的单元测试可与unittest模块一起使用。 我们将通过开发我们自己的阶乘函数来学习单元测试。 首先,打开一个新的Python文件,添加以下代码:

import unittest




class MyTestCase(unittest.TestCase):
    def test_something(self):
        self.assertEqual(True, False)




if __name__ == '__main__':
    unittest.main()

On line 1 you can notice the imported unittest module. On lines 4-6 we define a test case by extending the TestCase class. In it, we only have 1 test at the moment, the test_something. It is important that tests are prefixed with test_, so that the test runner can find them. Finally, on lines 9-10 we execute the tests. If you try running this file now, the test will fail:

在第1行上,您可以注意到导入的unittest模块。 在第4-6行,我们通过扩展TestCase类来定义测试用例。 其中,我们目前只有1个测试,即test_something 。 重要的是,测试必须以test_作为前缀,以便测试运行器可以找到它们。 最后,在9-10行,我们执行测试。 如果尝试立即运行此文件,则测试将失败:

======================================================================
FAIL: test_something (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "main.py", line 6, in test_something
    self.assertEqual(True, False)
AssertionError: True != False


----------------------------------------------------------------------
Ran 1 test in 0.001s


FAILED (failures=1)

That is happening because on line 6 we are asserting that True and False are equal, which is obviously false. Let's change that to test our (unwritten!) function:

之所以会这样,是因为在第6行中,我们断言TrueFalse相等,这显然是错误的。 让我们更改它以测试我们的(未编写!)功能:

class FactorialTestCase(unittest.TestCase):
    def test_factorial(self):
        result = factorial(3)
        self.assertEqual(result, 6)

If you run this now, it will still fail, because we did not write the factorial function yet. Let's make this test pass:

如果您现在运行此命令,它将仍然会失败,因为我们尚未编写factorial函数。 让我们通过这个测试:

def factorial(num): 
return 6

While this is not mathematically sound, this makes our test pass. Let’s write some more tests:

尽管这在数学上并不合理,但这使我们的测试通过了。 让我们再写一些测试:

def test_factorial(self): 
self.assertEqual(factorial(1), 1)
self.assertEqual(factorial(2), 2)
self.assertEqual(factorial(3), 6)
self.assertEqual(factorial(10), 3628800)

Check that the tests now fail (since factorial returns 6 all the time) and make the changes to the factorial function:

检查测试现在是否失败(因为factorial始终返回6),并对阶乘函数进行更改:

def factorial(num): 
if num == 1:
return 1
return num * factorial(num - 1)

And, if you run this, the tests pass:

而且,如果运行此命令,则测试通过:

Ran 1 test in 0.002s OK

Now, let’s take a step back and understand what we just did.

现在,让我们退后一步,了解我们刚才所做的事情。

断言 (Assertions)

When we write tests, we want to test for something. If something is equal, or not equal, if a function throws an exception, if a method was called with certain parameters, etc. Such checks are called assertions. Assertions are things that are always supposed to be true for the code to work properly.

编写测试时,我们想测试一些东西。 如果相等或不相等,则函数抛出异常,使用特定参数调用方法等。此类检查称为断言。 断言是为了使代码正常工作而总是应该做的事情。

One assertion you just saw if the assertEqual. It checks that 2 variables passed in are equal to each other. assertEqual is available through self, and is provided by the TestCase parent class. Here are some of the common assertions you will find useful:

您刚刚看到了一个断言是否为assertEqual 。 它检查传入的2个变量是否相等。 assertEqual通过self可用,由TestCase父类提供。 以下是一些有用的常见断言:

  • assertEqual(x, y)/assertNotEqual(x, y)

    assertEqual(x, y)/assertNotEqual(x, y)

  • assertTrue(x)/assertFalse(x)

    assertTrue(x)/assertFalse(x)

  • assertIs(x, y)

    assertIs(x, y)

  • assertIsNone(x)

    assertIsNone(x)

  • assertIn(x, y)

    assertIn(x, y)

  • assertIsInstance(x, y)/assertNotIsInstance(x, y)

    assertIsInstance(x, y)/assertNotIsInstance(x, y)

  • assertRaises(exc, fun, *args, **kwargs)

    assertRaises(exc, fun, *args, **kwargs)

  • assertGreater(x, y)/assertLess(x, y)/assertGreaterEqual(x, y)/assertLessEqual(x, y)

    assertGreater(x, y)/assertLess(x, y)/assertGreaterEqual(x, y)/assertLessEqual(x, y)

Some of these are available using a context manager, like assertRaises. It makes the code a bit easier to read:

其中一些可以使用上下文管理器来使用,例如assertRaises 。 它使代码更易于阅读:

with self.assertRaises(Exception):
do_something_that_throws("please")

Using these assertions you can test pretty much anything!

使用这些断言,您几乎可以测试任何东西!

模拟 (Mocking)

Recall that unit testing is testing individual parts of system in isolation. But, this is never the case with the systems we design. Different parts depend on other parts, and to make testing possible you sometimes need to mock them out. For example, if you are testing the behaviour of an HTTP response parser, there is no need to perform an actual HTTP response: all you need to do it mock it.

回想一下,单元测试在隔离测试系统的各个部分。 但是,我们设计的系统从来没有这种情况。 不同的部分取决于其他部分,为了使测试成为可能,您有时需要将它们模拟出来。 例如,如果您正在测试HTTP响应解析器的行为,则无需执行实际的HTTP响应:只需对其进行模拟即可。

Mocking, then, is a method of abstracting implementation of software modules that are not relevant to the behaviour you are testing. Moreover, you can assert if a mock was called, how many times was it called, and what arguments were supplied to it. These additional assertions will, no doubt, make your tests more robust.

那么,模拟是一种抽象化与所测试行为无关的软件模块实现的方法。 此外,您可以断言是否调用了模拟程序,调用了多少次以及向其提供了哪些参数。 毫无疑问,这些额外的断言将使您的测试更加强大。

Mocking in Python is also available via the unittest module. Let's start with a simple example. Suppose you wrote a function that calls the supplied callback:

也可以通过unittest模块使用Python进行模拟。 让我们从一个简单的例子开始。 假设您编写了一个调用提供的回调的函数:

def call_this_function(func): 
func()

To test it, you just need to pass a MagicMock (available through unittest.mock):

要对其进行测试,您只需传递一个MagicMock (可通过unittest.mock ):

import unittest
from unittest.mock import MagicMock




def call_this_function(func):
    func()




class CallbackTestCase(unittest.TestCase):
    def test_callback(self):
        mock = MagicMock()
        call_this_function(mock)
        mock.assert_called_once()






if __name__ == '__main__':
    unittest.main()

MagicMock is a special type of object. You can call it itself, call any method you can come up with and it will never throw. Instead, it will memorize all calls and make them available to you through assertions. Note that in this case, assertions are called on the mock object, instead of self. Here are some of the assertions available for mock objects:

MagicMock是一种特殊的对象。 您可以调用它本身,调用可以使用的任何方法,并且它永远不会抛出。 相反,它将记住所有调用,并通过断言使您可以使用它们。 请注意,在这种情况下,断言是在mock对象上调用的,而不是self 。 以下是一些可用于模拟对象的断言:

  • assert_called

    assert_called

  • assert_called_once

    assert_called_once

  • assert_called_with

    assert_called_with

  • assert_called_once_with

    assert_called_once_with

  • assert_not_called

    assert_not_called

模拟进口 (Mocking imports)

Sometimes, when you are testing a class, you need to mock out a certain function or class that is imported in it. Consider this example:

有时,当您测试一个类时,您需要模拟其中导入的某个函数或类。 考虑以下示例:

from some_library import api_action


def do_stuff():
  result = api_action('/stuff')
  return result

Now, to test this code, you want to mock the api_action function. This is actually very easy to do with the patch decorator (available from unittest.mock):

现在,要测试此代码,您想模拟api_action函数。 使用patch装饰器实际上很容易做到(可从unittest.mock ):

import unittest
from unittest.mock import patch
from main import do_stuff




class ApiTestCase(unittest.TestCase):
    @patch('main.api_action', side_effect='abacaba')
    def test_stuff(self, api_action_mock):
        result = do_stuff()
        self.assertEqual(result, 'abacaba')
        api_action_mock.assert_called_once_with('/stuff')






if __name__ == '__main__':
    unittest.main()

The patch decorator is applied to the test function that we are working with. It accepts as an argument the path to the mocked module. There is a very important note here: you specify the path relative to the testing code. For example, the main.py file imported the api_action function from some_library. Then, we import the main module into our test file. This means that we mock the api_action function that is imported inside the main, hence the 'main.api_action' path. If we were to write 'some_library.api_action', this would not work. We also specify the side_effect argument, which is essentially the return value that will be returned by the mock. The mock object is then passed as an argument to test function so we can assert on it. We assert that (1) the return value is forwarded and (2) that api_action is called on the correct endpoint.

patch装饰器将应用于我们正在使用的测试功能。 它接受模拟模块的路径作为参数。 这里有一个非常重要的说明:您指定相对于测试代码的路径。 例如, main.py文件从some_library导入了api_action函数。 然后,我们将main模块导入到我们的测试文件中。 这意味着我们模拟了在main内部导入的api_action函数,因此是'main.api_action'路径。 如果我们要写'some_library.api_action' ,那将行不通。 我们还指定了side_effect参数,该参数本质上是模拟将返回的返回值。 然后将模拟对象作为参数传递给测试函数,以便我们对其进行断言。 我们断言(1)转发了返回值,并且(2)在正确的端点上调用了api_action

结束语 (Closing notes)

Thank you for reading this article, I hope you liked it. Let me know in the comments about your experience with testing in Python!

感谢您阅读本文,希望您喜欢它。 在评论中让我知道您的Python测试经验!

翻译自: https://levelup.gitconnected.com/unit-testing-in-python-if-you-do-not-test-your-code-no-one-will-6504c7cda7d1

python中的单元测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值