分配抽象不变特性的模拟

问题描述:

我工作的一个项目用的类层次是这样的:分配抽象不变特性的模拟

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