test helper improvements (#9242)

pull/9259/head
Alexei Znamensky 2024-12-14 12:06:41 +13:00 committed by GitHub
parent 9df4ef9a9c
commit 88ea025d12
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 26 deletions

View File

@ -13,20 +13,25 @@ import yaml
import pytest import pytest
from ansible.module_utils.common._collections_compat import Sequence
class Helper(object): class Helper(object):
TEST_SPEC_VALID_SECTIONS = ["anchors", "test_cases"]
@staticmethod @staticmethod
def from_list(test_module, ansible_module, test_cases): def from_spec(test_module, ansible_module, test_spec, mocks=None):
helper = Helper(test_module, ansible_module, test_cases=test_cases) helper = Helper(test_module, ansible_module, test_spec=test_spec, mocks=mocks)
return helper return helper
@staticmethod @staticmethod
def from_file(test_module, ansible_module, filename): def from_file(test_module, ansible_module, filename, mocks=None):
with open(filename, "r") as test_cases: with open(filename, "r") as test_cases:
test_cases_data = yaml.safe_load(test_cases) test_spec = yaml.safe_load(test_cases)
return Helper.from_list(test_module, ansible_module, test_cases_data) return Helper.from_spec(test_module, ansible_module, test_spec, mocks)
@staticmethod @staticmethod
def from_module(ansible_module, test_module_name, test_spec=None): def from_module(ansible_module, test_module_name, test_spec=None, mocks=None):
test_module = sys.modules[test_module_name] test_module = sys.modules[test_module_name]
if test_spec is None: if test_spec is None:
test_spec = test_module.__file__.replace('.py', '.yaml') test_spec = test_module.__file__.replace('.py', '.yaml')
@ -35,14 +40,22 @@ class Helper(object):
def add_func_to_test_module(self, name, func): def add_func_to_test_module(self, name, func):
setattr(self.test_module, name, func) setattr(self.test_module, name, func)
def __init__(self, test_module, ansible_module, test_cases): def __init__(self, test_module, ansible_module, test_spec, mocks=None):
self.test_module = test_module self.test_module = test_module
self.ansible_module = ansible_module self.ansible_module = ansible_module
self.test_cases = [] self.test_cases = []
self.fixtures = {} self.fixtures = {}
if isinstance(test_spec, Sequence):
test_cases = test_spec
else: # it is a dict
test_cases = test_spec['test_cases']
spec_diff = set(test_spec.keys()) - set(self.TEST_SPEC_VALID_SECTIONS)
if spec_diff:
raise ValueError("Test specification contain unknown keys: {0}".format(", ".join(spec_diff)))
self.mocks_map = {m.name: m for m in mocks} if mocks else {}
for test_case in test_cases: for test_case in test_cases:
tc = ModuleTestCase.make_test_case(test_case, test_module) tc = ModuleTestCase.make_test_case(test_case, test_module, self.mocks_map)
self.test_cases.append(tc) self.test_cases.append(tc)
self.fixtures.update(tc.fixtures) self.fixtures.update(tc.fixtures)
self.set_test_func() self.set_test_func()
@ -99,7 +112,7 @@ class ModuleTestCase:
self.id = id self.id = id
self.input = input self.input = input
self.output = output self.output = output
self._mocks = mocks self.mock_specs = mocks
self.mocks = {} self.mocks = {}
self.flags = flags self.flags = flags
@ -124,23 +137,23 @@ class ModuleTestCase:
) )
@staticmethod @staticmethod
def make_test_case(test_case, test_module): def make_test_case(test_case_spec, test_module, mocks_map):
tc = ModuleTestCase( tc = ModuleTestCase(
id=test_case["id"], id=test_case_spec["id"],
input=test_case.get("input", {}), input=test_case_spec.get("input", {}),
output=test_case.get("output", {}), output=test_case_spec.get("output", {}),
mocks=test_case.get("mocks", {}), mocks=test_case_spec.get("mocks", {}),
flags=test_case.get("flags", {}) flags=test_case_spec.get("flags", {})
) )
tc.build_mocks(test_module) tc.build_mocks(test_module, mocks_map)
return tc return tc
def build_mocks(self, test_module): def build_mocks(self, test_module, mocks_map):
for mock, mock_spec in self._mocks.items(): for mock_name, mock_spec in self.mock_specs.items():
mock_class = self.get_mock_class(test_module, mock) mock_class = mocks_map.get(mock_name, self.get_mock_class(test_module, mock_name))
self.mocks[mock] = mock_class.build_mock(mock_spec) self.mocks[mock_name] = mock_class.build_mock(mock_spec)
self._fixtures.update(self.mocks[mock].fixtures()) self._fixtures.update(self.mocks[mock_name].fixtures())
@staticmethod @staticmethod
def get_mock_class(test_module, mock): def get_mock_class(test_module, mock):
@ -187,6 +200,10 @@ class ModuleTestCase:
class TestCaseMock: class TestCaseMock:
@property
def name(self):
raise NotImplementedError()
@classmethod @classmethod
def build_mock(cls, mock_specs): def build_mock(cls, mock_specs):
return cls(mock_specs) return cls(mock_specs)
@ -205,6 +222,10 @@ class TestCaseMock:
class RunCommandMock(TestCaseMock): class RunCommandMock(TestCaseMock):
@property
def name(self):
return "run_command"
def __str__(self): def __str__(self):
return "<RunCommandMock specs={specs}>".format(specs=self.mock_specs) return "<RunCommandMock specs={specs}>".format(specs=self.mock_specs)
@ -214,7 +235,7 @@ class RunCommandMock(TestCaseMock):
def fixtures(self): def fixtures(self):
@pytest.fixture @pytest.fixture
def patch_bin(mocker): def patch_bin(mocker):
def mockie(self, path, *args, **kwargs): def mockie(self_, path, *args, **kwargs):
return "/testbin/{0}".format(path) return "/testbin/{0}".format(path)
mocker.patch('ansible.module_utils.basic.AnsibleModule.get_bin_path', mockie) mocker.patch('ansible.module_utils.basic.AnsibleModule.get_bin_path', mockie)

View File

@ -475,4 +475,4 @@ TEST_CASES = [
), ),
] ]
Helper.from_list(sys.modules[__name__], snap, TEST_CASES) Helper.from_spec(sys.modules[__name__], snap, TEST_CASES)

View File

@ -7,7 +7,7 @@ __metaclass__ = type
from ansible_collections.community.general.plugins.modules import xfconf_info from ansible_collections.community.general.plugins.modules import xfconf_info
from .helper import Helper, RunCommandMock # pylint: disable=unused-import from .helper import Helper, RunCommandMock
Helper.from_module(xfconf_info, __name__) Helper.from_module(xfconf_info, __name__, mocks=[RunCommandMock])

View File

@ -4,6 +4,9 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
--- ---
anchors:
environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
test_cases:
- id: test_simple_property_get - id: test_simple_property_get
input: input:
channel: xfwm4 channel: xfwm4
@ -14,7 +17,7 @@
mocks: mocks:
run_command: run_command:
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity] - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity]
environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true} environ: *env-def
rc: 0 rc: 0
out: "100\n" out: "100\n"
err: "" err: ""