2017-10-10 49 views
3

我工作的一個項目用的類層次是這樣的:分配抽象不變特性的模擬

% 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)屬性?

回答

1

正如您發現的那樣,無法爲具有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'