[mocking-machinery] Add inject YAQL functions
Functions for injecting class methods or objects with mocks are added: * mock can be provided as method name * mock can be provided as YAQL expression * Also, withOriginal function is added. withOriginal allows to use data from the original context in mock function. Targets blueprint mock-context-manager Depends-On: I9a55d07188ff06bdf98248f011a700c297e33417 Change-Id: Iefee767390209bfad0cca4f39389e0205cd3c9e2
This commit is contained in:
parent
fd59720b1b
commit
d321757468
@ -33,6 +33,7 @@ CTX_THIS = '$?this'
|
||||
CTX_TYPE = '$?type'
|
||||
CTX_VARIABLE_SCOPE = '$?variableScope'
|
||||
CTX_YAQL_ENGINE = '$?yaqlEngine'
|
||||
CTX_ORIGINAL_CONTEXT = '$?originalContext'
|
||||
|
||||
DM_OBJECTS = 'Objects'
|
||||
DM_OBJECTS_COPY = 'ObjectsCopy'
|
||||
|
@ -13,7 +13,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from yaql.language import specs
|
||||
from yaql.language import yaqltypes
|
||||
|
||||
from murano.common import engine
|
||||
from murano.dsl import constants
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import linked_context
|
||||
from murano.dsl import yaql_integration
|
||||
|
||||
@ -68,3 +75,80 @@ class MockContextManager(engine.ContextManager):
|
||||
else:
|
||||
result_context = original_context
|
||||
return result_context
|
||||
|
||||
def create_root_context(self, runtime_version):
|
||||
root_context = super(MockContextManager, self).create_root_context(
|
||||
runtime_version)
|
||||
constext = root_context.create_child_context()
|
||||
constext.register_function(inject_method_with_str, name='inject')
|
||||
constext.register_function(inject_method_with_yaql_expr,
|
||||
name='inject')
|
||||
constext.register_function(with_original)
|
||||
return constext
|
||||
|
||||
|
||||
@specs.parameter('kwargs', yaqltypes.Lambda(with_context=True))
|
||||
def with_original(context, **kwargs):
|
||||
new_context = context.create_child_context()
|
||||
|
||||
original_context = context[constants.CTX_ORIGINAL_CONTEXT]
|
||||
for k, v in kwargs.iteritems():
|
||||
new_context['$' + k] = v(original_context)
|
||||
return new_context
|
||||
|
||||
|
||||
@specs.parameter('target',
|
||||
dsl.OneOf(dsl.MuranoTypeName(), dsl_types.MuranoObject))
|
||||
@specs.parameter('target_method', yaqltypes.String())
|
||||
@specs.parameter('mock_object', dsl_types.MuranoObject)
|
||||
@specs.parameter('mock_name', yaqltypes.String())
|
||||
def inject_method_with_str(context, target, target_method,
|
||||
mock_object, mock_name):
|
||||
ctx_manager = helpers.get_executor(context).context_manager
|
||||
|
||||
current_class = helpers.get_type(context)
|
||||
mock_func = current_class.find_single_method(mock_name)
|
||||
|
||||
if isinstance(target, dsl_types.MuranoClassReference):
|
||||
original_class = target.murano_class
|
||||
else:
|
||||
original_class = target.type
|
||||
|
||||
original_function = original_class.find_single_method(target_method)
|
||||
result_fd = original_function.yaql_function_definition.clone()
|
||||
|
||||
def payload_adapter(__context, __sender, *args, **kwargs):
|
||||
executor = helpers.get_executor(__context)
|
||||
return mock_func.invoke(
|
||||
executor, mock_object, args, kwargs, __context, True)
|
||||
|
||||
result_fd.payload = payload_adapter
|
||||
existing_mocks = ctx_manager.class_mock_ctx.setdefault(
|
||||
original_class.name, [])
|
||||
existing_mocks.append(result_fd)
|
||||
|
||||
|
||||
@specs.parameter('target',
|
||||
dsl.OneOf(dsl.MuranoTypeName(), dsl_types.MuranoObject))
|
||||
@specs.parameter('target_method', yaqltypes.String())
|
||||
@specs.parameter('expr', yaqltypes.Lambda(with_context=True))
|
||||
def inject_method_with_yaql_expr(context, target, target_method, expr):
|
||||
ctx_manager = helpers.get_executor(context).context_manager
|
||||
if isinstance(target, dsl_types.MuranoClassReference):
|
||||
original_class = target.murano_class
|
||||
else:
|
||||
original_class = target.type
|
||||
|
||||
original_function = original_class.find_single_method(target_method)
|
||||
result_fd = original_function.yaql_function_definition.clone()
|
||||
|
||||
def payload_adapter(__super, __context, __sender, *args, **kwargs):
|
||||
new_context = context.create_child_context()
|
||||
new_context[constants.CTX_ORIGINAL_CONTEXT] = __context
|
||||
return expr(new_context, __sender, *args, **kwargs)
|
||||
|
||||
result_fd.payload = payload_adapter
|
||||
result_fd.insert_parameter('__super', yaqltypes.Super())
|
||||
existing_mocks = ctx_manager.class_mock_ctx.setdefault(
|
||||
original_class.name, [])
|
||||
existing_mocks.append(result_fd)
|
||||
|
59
murano/tests/unit/engine/meta/TestMock.yaml
Normal file
59
murano/tests/unit/engine/meta/TestMock.yaml
Normal file
@ -0,0 +1,59 @@
|
||||
Name: TestMocks
|
||||
|
||||
Methods:
|
||||
|
||||
mock1:
|
||||
Body:
|
||||
- Return: 'This is mock1'
|
||||
|
||||
testInjectMethodWithString:
|
||||
Body:
|
||||
- inject(TestMocksFixture, simpleMethod1, $this, mock1)
|
||||
|
||||
- $originalClass: new(TestMocksFixture)
|
||||
- $returnValue: $originalClass.simpleMethod1()
|
||||
- trace($returnValue)
|
||||
|
||||
testInjectObjectWithString:
|
||||
Body:
|
||||
- $originalClass: new(TestMocksFixture)
|
||||
- inject($originalClass, simpleMethod1, $this, mock1)
|
||||
|
||||
- $returnValue: $originalClass.simpleMethod1()
|
||||
- trace($returnValue)
|
||||
|
||||
testInjectMethodWithYaqlExpr:
|
||||
Body:
|
||||
- $originalClass: new(TestMocksFixture)
|
||||
# Calling original method without mocking
|
||||
- trace($originalClass.simpleMethod1())
|
||||
|
||||
- $mockText: 'I am mock'
|
||||
- inject(TestMocksFixture, simpleMethod1, trace($mockText))
|
||||
|
||||
# Calling original method after mocking
|
||||
- $originalClass.simpleMethod1()
|
||||
|
||||
testInjectObjectWithYaqlExpr:
|
||||
Body:
|
||||
- $originalClass: new(TestMocksFixture)
|
||||
# Calling original method without mocking
|
||||
- trace($originalClass.simpleMethod1())
|
||||
|
||||
- $mockText: 'I am mock'
|
||||
- inject($originalClass, simpleMethod1, trace($mockText))
|
||||
|
||||
# Calling original method after mocking
|
||||
- $originalClass.simpleMethod1()
|
||||
|
||||
testWithoriginal:
|
||||
Body:
|
||||
- $originalClass: new(TestMocksFixture)
|
||||
- inject(TestMocksFixture, simpleMethod1, withOriginal(t => $originalClass.someProperty) -> trace($t))
|
||||
- $originalClass.simpleMethod1()
|
||||
|
||||
testOriginalMethod:
|
||||
Body:
|
||||
- $originalClass: new(TestMocksFixture)
|
||||
- inject(TestMocksFixture, simpleMethod1, originalMethod())
|
||||
- trace($originalClass.simpleMethod1())
|
11
murano/tests/unit/engine/meta/TestMockFixture.yaml
Normal file
11
murano/tests/unit/engine/meta/TestMockFixture.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
Name: TestMocksFixture
|
||||
|
||||
Properties:
|
||||
someProperty:
|
||||
Contract: $.string().notNull()
|
||||
Default: DEFAULT
|
||||
|
||||
Methods:
|
||||
simpleMethod1:
|
||||
Body:
|
||||
- Return: 'method1'
|
@ -14,9 +14,16 @@ import mock
|
||||
from yaql import contexts
|
||||
from yaql import specs
|
||||
|
||||
from murano.dsl import constants
|
||||
from murano.dsl import executor
|
||||
from murano.dsl import murano_class
|
||||
from murano.engine import environment
|
||||
from murano.engine import mock_context_manager
|
||||
from murano.tests.unit import base
|
||||
from murano.tests.unit.dsl.foundation import object_model as om
|
||||
from murano.tests.unit.dsl.foundation import runner
|
||||
from murano.tests.unit.dsl.foundation import test_case
|
||||
|
||||
|
||||
FIXTURE_CLASS = 'io.murano.system.Agent'
|
||||
FIXTURE_FUNC = 'call'
|
||||
@ -26,6 +33,34 @@ def _get_fd(set_to_extract):
|
||||
return list(set_to_extract)[0]
|
||||
|
||||
|
||||
class TestMockContextManager(mock_context_manager.MockContextManager):
|
||||
def __init__(self, functions):
|
||||
super(TestMockContextManager, self).__init__()
|
||||
self.__functions = functions
|
||||
|
||||
def create_root_context(self, runtime_version):
|
||||
root_context = super(TestMockContextManager, self).create_root_context(
|
||||
runtime_version)
|
||||
context = root_context.create_child_context()
|
||||
for name, func in self.__functions.iteritems():
|
||||
context.register_function(func, name)
|
||||
return context
|
||||
|
||||
|
||||
class MockRunner(runner.Runner):
|
||||
def __init__(self, model, package_loader, functions):
|
||||
if isinstance(model, basestring):
|
||||
model = om.Object(model)
|
||||
model = om.build_model(model)
|
||||
if 'Objects' not in model:
|
||||
model = {'Objects': model}
|
||||
|
||||
self.executor = executor.MuranoDslExecutor(
|
||||
package_loader, TestMockContextManager(functions),
|
||||
environment.Environment())
|
||||
self._root = self.executor.load(model).object
|
||||
|
||||
|
||||
class TestMockManager(base.MuranoTestCase):
|
||||
|
||||
def test_create_class_context(self):
|
||||
@ -54,3 +89,41 @@ class TestMockManager(base.MuranoTestCase):
|
||||
# Mock function should go first, but result context should contain both
|
||||
self.assertIs(mock_function, _get_fd(all_functions[0]))
|
||||
self.assertIs(original_function, _get_fd(all_functions[1]))
|
||||
|
||||
def test_create_root_context(self):
|
||||
mock_manager = mock_context_manager.MockContextManager()
|
||||
ctx_to_check = mock_manager.create_root_context(
|
||||
constants.RUNTIME_VERSION_1_1)
|
||||
inject_count = ctx_to_check.collect_functions('inject')
|
||||
with_original_count = ctx_to_check.collect_functions('withOriginal')
|
||||
|
||||
self.assertEqual(2, len(inject_count[0]))
|
||||
self.assertEqual(1, len(with_original_count[0]))
|
||||
|
||||
|
||||
class TestMockYaqlFunctions(test_case.DslTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestMockYaqlFunctions, self).setUp()
|
||||
self.runner = MockRunner(om.Object('TestMocks'),
|
||||
self.package_loader, self._functions)
|
||||
|
||||
def test_inject_method_with_str(self):
|
||||
self.runner.testInjectMethodWithString()
|
||||
self.assertEqual(['This is mock1'], self.traces)
|
||||
|
||||
def test_inject_object_with_str(self):
|
||||
self.runner.testInjectObjectWithString()
|
||||
self.assertEqual(['This is mock1'], self.traces)
|
||||
|
||||
def test_inject_method_with_yaql_expr(self):
|
||||
self.runner.testInjectMethodWithYaqlExpr()
|
||||
self.assertEqual(['method1', 'I am mock'], self.traces)
|
||||
|
||||
def test_inject_object_with_yaql_expr(self):
|
||||
self.runner.testInjectObjectWithYaqlExpr()
|
||||
self.assertEqual(['method1', 'I am mock'], self.traces)
|
||||
|
||||
def test_with_original(self):
|
||||
self.runner.testWithoriginal()
|
||||
self.assertEqual(['DEFAULT'], self.traces)
|
||||
|
24
releasenotes/notes/enable-mocks-a156e7cc1b1d5066.yaml
Normal file
24
releasenotes/notes/enable-mocks-a156e7cc1b1d5066.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
features:
|
||||
- Enable mocks in MuranoPL tests cases.
|
||||
Those test cases are run with murano-test-tunner.
|
||||
To enable mocks use the one of the *inject* YAQL functions
|
||||
* `def inject(target, target_method, mock_object, mock_name)`
|
||||
* `def inject(target, target_method, yaql_expr)`
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
* *target* MuranoPL class name (namespaces can be used or full class name
|
||||
in quotes) or MuranoPL object
|
||||
|
||||
* *target_method* Method name to mock in target
|
||||
|
||||
* *mock_object* Object, where mock definition is contained
|
||||
|
||||
* *mock_name* Name of method, where mock definition is contained
|
||||
|
||||
* *yaql_expr* YAQL expression, parameters are allowed
|
||||
For more information, please follow the corresponding article in the
|
||||
Murano developing applications guide.
|
||||
|
Loading…
Reference in New Issue
Block a user