分配抽象不变特性的模拟
问题描述:
我工作的一个项目用的类层次是这样的:分配抽象不变特性的模拟
% Standard class that has properties and methods shared by all classes in the project
classdef StandardClass < handle
properties (Constant, Abstract)
displayName
end
end
% Abstract class that defines the interface for a particular set of concrete classes
classdef AbstractClass < StandardClass
methods
function name = getDisplayName(obj)
name = obj.displayName;
end
end
end
% Actual implementation of the concrete class
classdef ConcreteClass < AbstractClass
properties (Constant)
displayName = 'My Concrete Class'
end
end
有了这个MWE,我可以创建一个具体类的实例,我可以查询displayName。
现在,我想测试另一个类与我的AbstractClass
接口。而不是写一个测试,通过每个具体类循环和测试界面,我想使用新的模拟框架使用,使一个模拟我AbstractClass
作为模板:
classdef UnitTest < matlab.mock.TestCase
methods (Test)
function testDisplay(obj)
concrete = createMock(obj, ?AbstractClass);
% My test here
end
end
end
这与错误结束:
---------
Error ID:
---------
'MATLAB:mock:MockContext:NonDefaultPropertyAttribute'
--------------
Error Details:
--------------
Error using matlab.mock.internal.MockContext>validateAbstractProperties (line 623)
Unable to create a mock for the 'AbstractClass' class because Abstract property 'displayName' has a non-default value for its 'Constant' attribute.
那么,这是一个无赖。不过,我想我明白了。该模拟无法知道如何分配抽象属性,所以我补充说:
properties (Constant)
displayName = 'Abstract Class';
end
我的AbstractClass
。然后当我运行我的测试时,我的模拟被创建并且单元测试运行良好。但是,如果我然后去尝试创建测试框架之外的具体类:
>> test = ConcreteClass();
Error using ConcreteClass
Cannot define property 'displayName' in class 'ConcreteClass' because the property has already been defined in the superclass 'AbstractClass'.
因此,我陷入了一个catch-22。只有当我破坏了代码才能运行,我就可以让模拟工作并成功运行单元测试。
我正在使用的代码库有很多不同的抽象类,它们都是基于我们的标准类。有什么方法可以告诉模拟框架在使用模板调用createMock
时如何分配(Abstract, Constant)
属性?
答
正如您发现的那样,无法为具有Abstract,Constant属性的类创建模拟,但这是我们将在未来版本中考虑的一项功能。
这里有一个解决方法:
由于MATLAB R2017b的,你可以创建类的嘲弄与抽象的,静态方法: https://www.mathworks.com/help/matlab/release-notes.html?searchHighlight=Mocking%20Framework%3A&s_tid=doc_srchtitle
所以,你可以重构代码使用静态方法,而不是不变的性质:
% Standard class that has properties and methods shared by all classes in the project
classdef StandardClass < handle
methods (Abstract, Hidden, Static)
name = getDisplayNameInternal
end
end
% Abstract class that defines the interface for a particular set of concrete classes
classdef AbstractClass < StandardClass
methods
function name = getDisplayName(obj)
name = obj.getDisplayNameInternal;
end
end
end
% Actual implementation of the concrete class
classdef ConcreteClass < AbstractClass
methods (Hidden, Static)
function name = getDisplayNameInternal
name = 'My Concrete Class';
end
end
end
然后,您可以创建一个模拟:
[concrete, behavior] = createMock(obj, ?AbstractClass);
assignOutputsWhen(obj, withAnyInputs(behavior.getDisplayNameInternal), 'My mock class');
它实现了所需的行为:
>> concrete.getDisplayName
ans =
'My mock class'