Remplacer les fonctions intégrées par une simulation au moment du test sans DI

open_repeatedly.py


import time

def open_repeatedly(path, retries=5, retry_interval=1.0):
    while True:
        try:
            return open(path)
        except OSError:
            if retries <= 0:
                raise
            if retry_interval > 0:
                time.sleep(retry_interval)
            retries -= 1

Je ne sais pas trop pour quoi l'utiliser, mais si vous avez une fonction comme celle-ci, j'aimerais tester son comportement exceptionnel. Par exemple, si retry_interval vaut 0, `` time.sleep () '' ne sera pas appelé.

Cependant, il est gênant de DI ```open () et time.sleep () . L'interface se salit. Je veux aussi éviter les ** kwargs ''.

Donc, on m'a dit sur Twitter «unittest.mock.patch».

test.py


import os
from tempfile import mkstemp
import unittest
from unittest.mock import call, patch, MagicMock

import open_repeatedly

class TestOpenRepeatedly(unittest.TestCase):
    def test_1(self):
        with patch('open_repeatedly.open') as mock:
            expected_ret = MagicMock()
            mock.return_value = expected_ret
            path = '/path/to/test.txt'
            f = open_repeatedly.open_repeatedly(path)
            self.assertEqual(f, expected_ret)
            mock.assert_called_with(path)

    def test_2(self):
        path = '/path/to/test.txt'
        with patch('open_repeatedly.open') as o_mock:
            o_mock.side_effect = OSError('Test')
            with patch('open_repeatedly.time.sleep') as s_mock:
                calls = [call(1.0)] * 5
                with self.assertRaises(OSError):
                    open_repeatedly.open_repeatedly(path)
                self.assertEqual(5, s_mock.call_count)
                s_mock.assert_has_calls(calls)
            self.assertEqual(6, o_mock.call_count)
            o_mock.assert_called_with(path)

    def test_3(self):
        path = '/path/to/test.txt'
        with patch('open_repeatedly.open') as o_mock:
            o_mock.side_effect = OSError('Test')
            with patch('open_repeatedly.time.sleep') as s_mock:
                with self.assertRaises(OSError):
                    open_repeatedly.open_repeatedly(
                        path, retry_interval=0)
                self.assertEqual(0, s_mock.call_count)
            self.assertEqual(6, o_mock.call_count)
            o_mock.assert_called_with(path)

    @patch('open_repeatedly.time.sleep')
    @patch('open_repeatedly.open')
    def test_4(self, o_mock, s_mock):
        path = '/path/to/test.txt'
        o_mock.side_effect = OSError('Test')
        with self.assertRaises(OSError):
            open_repeatedly.open_repeatedly(
                path, retries=0)
        self.assertEqual(0, s_mock.call_count)
        self.assertEqual(1, o_mock.call_count)
        o_mock.assert_called_with(path)


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

Les décorateurs sont plus faciles.

$ python -m unittest
....
----------------------------------------------------------------------
Ran 4 tests in 0.005s

OK

Sentir que je l'ai implémenté Il semble que cela fonctionne comme prévu. Est-ce correct pour savoir comment l'utiliser?

Recommended Posts

Remplacer les fonctions intégrées par une simulation au moment du test sans DI
Remplacez tout d'un coup par sed