From fdd3e5ce6d21d41d4aa34c0344d53d6ef7a26fb9 Mon Sep 17 00:00:00 2001 From: 100gle <569590461@qq.com> Date: Thu, 5 Jan 2023 09:27:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=8A=80=E8=83=BD?= =?UTF-8?q?=E6=89=A9=E5=B1=95N11=E4=B8=80=E7=AB=A0=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E6=BA=90=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code/newsletter/N11/tests/conftest.py | 79 +++++++++++++++++++ .../N11/tests/pytest_setup_teardown.py | 37 +++++++++ code/newsletter/N11/tests/test_accumulate.py | 27 +++++++ .../tests/test_accumulate_with_other_mark.py | 52 ++++++++++++ .../test_accumulate_with_parametrize_mark.py | 56 +++++++++++++ .../newsletter/N11/tests/test_with_fixture.py | 68 ++++++++++++++++ .../N11/tests/unittest_setup_teardown.py | 37 +++++++++ 7 files changed, 356 insertions(+) create mode 100644 code/newsletter/N11/tests/conftest.py create mode 100644 code/newsletter/N11/tests/pytest_setup_teardown.py create mode 100644 code/newsletter/N11/tests/test_accumulate.py create mode 100644 code/newsletter/N11/tests/test_accumulate_with_other_mark.py create mode 100644 code/newsletter/N11/tests/test_accumulate_with_parametrize_mark.py create mode 100644 code/newsletter/N11/tests/test_with_fixture.py create mode 100644 code/newsletter/N11/tests/unittest_setup_teardown.py diff --git a/code/newsletter/N11/tests/conftest.py b/code/newsletter/N11/tests/conftest.py new file mode 100644 index 0000000..ec884cb --- /dev/null +++ b/code/newsletter/N11/tests/conftest.py @@ -0,0 +1,79 @@ +import dataclasses + +import pytest + + +@dataclasses.dataclass +class Connection: + def execute(self, sql: str): + print(f"execute {sql}...") + return True + + def close(self): + print(f"\ndisconnect {repr(self)} object...") + + +@dataclasses.dataclass +class Database: + dialect: str = "sqlite" + + def connect(self): + return Connection() + + def close(self): + print(f"\ndisconnect {repr(self)} object...") + + +@pytest.fixture() +def sqlite(): + db = Database() + conn = db.connect() + try: + yield conn + finally: + conn.close() + db.close() + + +@pytest.fixture() +def mysql(): + db = Database(dialect="mysql") + conn = db.connect() + try: + yield conn + finally: + conn.close() + db.close() + + +@pytest.fixture(params=["sqlite", "mysql"]) +def database(request): + db = Database(dialect=request.param) + conn = db.connect() + try: + yield conn + finally: + conn.close() + db.close() + + +@pytest.fixture() +def gender(): + return ["male", "female"] + + +@pytest.fixture() +def cpu(): + return "cpu" + + +@pytest.fixture() +def device(cpu): + return f"the device with {cpu}" + + +@pytest.fixture(scope="class") +def logger(): + print("\nstart recording...") + yield + print("\nend recording...") diff --git a/code/newsletter/N11/tests/pytest_setup_teardown.py b/code/newsletter/N11/tests/pytest_setup_teardown.py new file mode 100644 index 0000000..ccc65cf --- /dev/null +++ b/code/newsletter/N11/tests/pytest_setup_teardown.py @@ -0,0 +1,37 @@ +import pytest + + +def setup_module(): + print("[module level]: setup") + + +def teardown_module(): + print("[module level]: teardown") + + +class TestFoo: + def setup(self, method) -> None: + print("\t\t[function level]: setup") + + def teardown(self, method) -> None: + print("\t\t[function level]: teardown") + + @classmethod + def setup_class(cls): + print("\t[class level]: setup") + + @classmethod + def teardown_class(cls): + print("\t[class level]: teardown") + + def test_case1(self): + print("\t\t\t--> test case 1 here") + assert True + + def test_case2(self): + print("\t\t\t--> test case 2 here") + assert True + + +if __name__ == '__main__': + pytest.main([__file__, "-s", "--no-header"]) diff --git a/code/newsletter/N11/tests/test_accumulate.py b/code/newsletter/N11/tests/test_accumulate.py new file mode 100644 index 0000000..07e6e93 --- /dev/null +++ b/code/newsletter/N11/tests/test_accumulate.py @@ -0,0 +1,27 @@ +from typing import Tuple, TypeVar + +import pytest + +Number = TypeVar("Number", int, float) + + +def accumulate(*numbers: Tuple[Number]) -> Number: + result = 0 + for n in numbers: + if not isinstance(n, (int, float)): + raise ValueError(f"{n} isn't a valid number value") + + result += n + + return result + + +def test_accumulate(): + numbers = [1, 2, 3, 4] + expected = 10 + + assert accumulate(*numbers) == expected + + +if __name__== '__main__': + pytest.main() diff --git a/code/newsletter/N11/tests/test_accumulate_with_other_mark.py b/code/newsletter/N11/tests/test_accumulate_with_other_mark.py new file mode 100644 index 0000000..81abd5b --- /dev/null +++ b/code/newsletter/N11/tests/test_accumulate_with_other_mark.py @@ -0,0 +1,52 @@ +import sys +from typing import Tuple, TypeVar + +import pytest + +Number = TypeVar("Number", int, float) +VERSION = "0.0.2" + + +def accumulate(*numbers: Tuple[Number]) -> Number: + result = 0 + for n in numbers: + if not isinstance(n, (int, float)): + raise ValueError(f"{n} isn't a valid number value") + + result += n + + return result + + +@pytest.mark.xfail() +@pytest.mark.parametrize("numbers, expected", argvalues=[([10, 11, -11, -12], 1)]) +def test_accumulate_with_failed(numbers, expected): + assert accumulate(*numbers) == expected + + +@pytest.mark.parametrize( + "numbers, expected", + argvalues=[ + pytest.param( + [10, 11, -11, -12], 1, marks=pytest.mark.xfail(reason="expected failed") + ) + ], +) +def test_accumulate_with_failed_mark(numbers, expected): + assert accumulate(*numbers) == expected + + +@pytest.mark.skip(reason="the case is WIP") +def test_wip_with_skip_mark(): + ... + + +def test_wip_with_skip_function(): + if VERSION <= '0.0.2': + pytest.skip("the API is still on WIP") + assert False + + +@pytest.mark.skipif(sys.platform != "linux", reason="only for linux") +def test_wip_function_only_for_linux(): + assert True diff --git a/code/newsletter/N11/tests/test_accumulate_with_parametrize_mark.py b/code/newsletter/N11/tests/test_accumulate_with_parametrize_mark.py new file mode 100644 index 0000000..c2b63d2 --- /dev/null +++ b/code/newsletter/N11/tests/test_accumulate_with_parametrize_mark.py @@ -0,0 +1,56 @@ +from typing import Tuple, TypeVar + +import pytest + +Number = TypeVar("Number", int, float) + + +def accumulate(*numbers: Tuple[Number]) -> Number: + result = 0 + for n in numbers: + if not isinstance(n, (int, float)): + raise ValueError(f"{n} isn't a valid number value") + + result += n + + return result + + +def test_accumulate_without_mark(): + numbers = [ + [1, 2, 3, 4], + [5, 6, 7, 8], + [-1, 0, 1, 0], + [10, 11, -11, -12], + ] + expected = (10, 26, 0, 1) + + for number, expect in zip(numbers, expected): + assert accumulate(*number) == expect + + +@pytest.mark.parametrize( + argnames="numbers,expected", + argvalues=[ + ([1, 2, 3, 4], 10), + ([5, 6, 7, 8], 26), + ([-1, 0, 1, 0], 0), + ([10, 11, -11, -12], 1), + ], + ids=list("abcd"), +) +def test_accumulate_with_parametrize_mark(numbers, expected): + assert accumulate(*numbers) == expected + + +@pytest.mark.parametrize(argnames="even_number", argvalues=[2, 4]) +@pytest.mark.parametrize(argnames="odd_number", argvalues=[1, 3]) +def test_accumulate_with_multiple_parametrize_mark(even_number, odd_number): + expected = { + "1+2": 3, + "1+4": 5, + "3+2": 5, + "3+4": 7, + } + expr = f"{odd_number}+{even_number}" + assert accumulate(odd_number, even_number) == expected[expr] diff --git a/code/newsletter/N11/tests/test_with_fixture.py b/code/newsletter/N11/tests/test_with_fixture.py new file mode 100644 index 0000000..c2d1ede --- /dev/null +++ b/code/newsletter/N11/tests/test_with_fixture.py @@ -0,0 +1,68 @@ +def test_sql_with_mock_sqlite_database(sqlite): + ok = sqlite.execute("SELECT 1;") + assert ok + + +def test_sql_with_mock_mysql_database(mysql): + ok = mysql.execute("SELECT * FROM mock_table;") + assert ok + + +def test_gender_fixture(gender): + print(f"Gender fixture value: {gender}") + assert ["male", "female"] == gender + + +class TestClassScopeFixture: + def test_foo(self, logger): + assert True + + def test_bar(self, logger): + assert True + + +class TestClassScopeFixture2: + def test_foo2(self, logger): + assert True + + def test_bar2(self, logger): + assert True + + +def test_with_mock_database(database): + sql = "SELECT * FROM mock_table;" + ok = database.execute(sql) + assert ok + + +def test_with_request_fixture(request): + from pprint import pformat + + props = [prop for prop in dir(request) if not prop.startswith("_")] + + print(f"request properties:\n{pformat(props)}") + assert True + + +def test_with_capsys_fixture(capsys): + def greet(name: str = ""): + msg = name or "World" + print(f"Hello, {msg}") + + greet() + captured = capsys.readouterr() + assert captured.out == "Hello, World\n" + + greet("100gle") + captured = capsys.readouterr() + assert captured.out == "Hello, 100gle\n" + + +def test_with_tmp_path_fixture(tmp_path): + import pathlib + + p = tmp_path.joinpath("foo") + print(f"\n{p}") + + assert isinstance(p, pathlib.Path) + assert p.stem == "foo" diff --git a/code/newsletter/N11/tests/unittest_setup_teardown.py b/code/newsletter/N11/tests/unittest_setup_teardown.py new file mode 100644 index 0000000..0fa3b50 --- /dev/null +++ b/code/newsletter/N11/tests/unittest_setup_teardown.py @@ -0,0 +1,37 @@ +import unittest + + +def setUpModule(): + print("[module level]: setup") + + +def tearDownModule(): + print("[module level]: teardown") + + +class TestFoo(unittest.TestCase): + def setUp(self) -> None: + print("\t\t[function level]: setup") + + def tearDown(self) -> None: + print("\t\t[function level]: teardown") + + @classmethod + def setUpClass(cls): + print("\t[class level]: setup") + + @classmethod + def tearDownClass(cls): + print("\t[class level]: teardown") + + def test_case1(self): + print("\t\t\t--> test case 1 here") + self.assertEqual(1, 1) + + def test_case2(self): + print("\t\t\t--> test case 2 here") + self.assertEqual(1, 1) + + +if __name__ == '__main__': + unittest.main()