[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:
Ekaterina Chernova 2015-12-03 17:55:41 +03:00
parent fd59720b1b
commit d321757468
6 changed files with 252 additions and 0 deletions

View File

@ -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'

View File

@ -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)

View 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())

View File

@ -0,0 +1,11 @@
Name: TestMocksFixture
Properties:
someProperty:
Contract: $.string().notNull()
Default: DEFAULT
Methods:
simpleMethod1:
Body:
- Return: 'method1'

View File

@ -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)

View 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.