最近在幫公司開發,使用 python 製作出 Windows 使用的小工具,順便 k 了一下單元測試,把遇到過的技巧做個筆記。
chan.py
import os
import shutil
import requests as requests
class Chan:
def add(self, number1: int, number2: int) -> int:
return number1 + number2
def raise_method(self, result: str) -> str:
if result != 'raise':
return result
raise Exception('exception raised')
def a(self) -> str:
return self.b()
def b(self) -> str:
return 'b'
def move_file(self, from_path, to_path) -> object:
return os.rename(from_path, to_path)
def get_url(self, url) -> int:
response = requests.get(url)
return response.status_code
def my_open(self, path) -> str:
with open(path) as f:
return f.read()
def copy_twice(self, source: str, dest_dir: str) -> None:
base_path = os.path.dirname(__file__)
shutil.copyfile(source, os.path.join(base_path, dest_dir, 'first'))
shutil.copyfile(source, os.path.join(base_path, dest_dir, 'second'))
def scan_folder(self, path) -> list:
files = []
for item in os.listdir(os.path.join(path)):
if os.path.isfile(os.path.join(path, item)):
files.append(os.path.join(path, item))
return files
tests/test_chan.py
import os.path
import unittest
from unittest.mock import patch, MagicMock, mock_open, call
from chan import Chan
def mock_response(*args, **kwargs) -> object:
class Response:
status_code = 500
return Response()
class MyTestCase(unittest.TestCase):
base_path: str
def setUp(self) -> None:
self.base_path = os.path.dirname(os.path.dirname(__file__))
def test_add(self) -> None:
chan = Chan()
actual = chan.add(1, 2)
self.assertEqual(3, actual)
def test_raise_should_not_happened(self) -> None:
chan = Chan()
actual = chan.raise_method('test')
self.assertEqual('test', actual)
def test_raise_should_happened(self) -> None:
chan = Chan()
self.assertRaises(Exception, chan.raise_method, 'raise')
def test_raise_error_message(self) -> None:
chan = Chan()
with self.assertRaises(Exception) as error:
chan.raise_method('raise')
self.assertEqual('exception raised', str(error.exception))
@patch.object(Chan, 'b', MagicMock(return_value='c'))
def test_mock_method(self) -> None:
chan = Chan()
actual = chan.a()
self.assertEqual('c', actual)
@patch.object(Chan, 'b')
def test_mock_method_called_once_by_injection(self, mock_b: MagicMock) -> None:
mock_b.return_value = 'c'
chan = Chan()
actual = chan.a()
self.assertEqual('c', actual)
self.assertTrue(mock_b.called)
def test_mock_method_called_once_by_with(self) -> None:
with patch.object(Chan, 'b') as check:
check.return_value = 'c'
chan = Chan()
actual = chan.a()
self.assertEqual('c', actual)
check.assert_called_once()
@patch('chan.os.rename', MagicMock(return_value='moved'))
def test_os_method(self) -> None:
chan = Chan()
actual = chan.move_file('1.txt', '2.txt')
self.assertEqual('moved', actual)
@patch('chan.requests.get', MagicMock(side_effect=mock_response))
def test_requests_get(self) -> None:
chan = Chan()
actual = chan.get_url('https://www.google.com')
self.assertEqual(500, actual)
@patch('chan.open', mock_open(read_data='ok'))
def test_open(self) -> None:
chan = Chan()
actual = chan.my_open('path')
self.assertEqual('ok', actual)
@patch('chan.open', new_callable=mock_open, read_data='ok')
def test_open_by_injection(self, m) -> None:
chan = Chan()
actual = chan.my_open('path')
self.assertEqual('ok', actual)
self.assertTrue(m.called)
@patch('chan.shutil.copyfile')
def test_copy_twice(self, mock_copyfile: MagicMock) -> None:
chan = Chan()
source = 'test.zip'
chan.copy_twice(source, 'test_path')
expected = [call(source, os.path.join(self.base_path, 'test_path', 'first')),
call(source, os.path.join(self.base_path, 'test_path', 'second'))]
self.assertEqual(expected, mock_copyfile.call_args_list)
self.assertEqual(2, mock_copyfile.call_count)
@patch('chan.os.path.isfile')
@patch('chan.os.listdir')
def test_scan_folder(self, mock_listdir: MagicMock, mock_isfile: MagicMock) -> None:
mock_listdir.return_value = ['dir1', 'file1', 'dir2', 'file2']
mock_isfile.side_effect = [False, True, False, True]
chan = Chan()
test_dir = 'test_dir'
actual = chan.scan_folder('test_dir')
expected = [os.path.join(test_dir, 'file1'), os.path.join(test_dir, 'file2')]
self.assertEqual(expected, actual)
if __name__ == '__main__':
unittest.main()