Can I set unit test TestParameter properties using functions not on the path?

10 views (last 30 days)
I would like to write a unit test for a method on an ENUM class, using all possible enumerations of that ENUM class as a TestParameter. The ENUM class, as well as its corresponding unit test, is part of a repository, so I'd like to be diligent when setting up the paths to ensure that neither the unit test nor the ENUM itself is dependent on any code I have locally on my machine (and similar for any developers). Therefore I'd like to utilize PathFixtures to handle adding and removing from the Matlab path. I can't figure out how to set the TestParameter properties given this setup, since the TestParameter block requires the ENUM class to be on the path.
The ENUM classdef m-file (MyClass.m) looks like this:
classdef MyClass
enumeration
A( 1, 2 )
B( 3, 4 )
end
properties
prop1
prop2
end
methods
function this = MyClass( input1, input2 )
this.prop1 = input1;
this.prop2 = input2;
end
function output = SomeMethod( this )
output = this.prop1 + this.prop2;
end
end
end
The unit test code (MyClassTest.m) looks like this:
classdef MyClassTest < matlab.unittest.TestCase
properties( TestParameter )
allPossibleEnums = arrayfun( @( x ) x, ...
eval( 'enumeration( ''MyClass'' )' ), ...
'UniformOutput', false );
end
methods( TestClassSetup = true )
function SetUpPaths( testCase )
testCase.applyFixture( matlab.unittest.fixtures.PathFixture( ...
PathToMyClass ) );
end
end
methods( Test = true, ParameterCombination = 'sequential' )
function SomeMethodTest( testCase, allPossibleEnums )
output = allPossibleEnums.SomeMethod();
check = allPossibleEnums.prop1 + allPossibleEnums.prop2;
testCase.verifyEqual( output, check );
end
end
end
My issue is that the properties block to define allPossibleEnums requires PathToMyClass be on the Matlab path prior to running the constructor for MyClassTest. I tried using a SharedTestFixtures for MyClassTest but that appears to require a hard-coded path, and I'd like other developers to be able to run the unit test without having to modify it when they check it out. I could also write a script and add the path to MyClass with an addpath( ... ) call prior to constructing the MyClassTest, but that loses the robustness I'd like when using a PathFixtures. I could also hard-code allPossibleEnums as a cell array of strings containing all the possible enumerations, but that would need to be updated by hand should the enumeration list change. And I could simply leave the TestParameter properties empty and just create the enumeration list within the SomeMethodTest method and write a "for" loop, but that would seem to defeat the purpose of having a "sequential" parameter option and wouldn't allow me to see which enumeration caused a failure (I would also have to repeat the creation of the enumeration list in any additional test methods).
Is there any way around this? Am I simply misunderstanding how to use the unit test framework or the PathFixtures? Is there a different/better way to list out all the possible enumerations of MyClass as a TestParameter?
Thanks!

Accepted Answer

Andy Campbell
Andy Campbell on 18 Jan 2017
Edited: Andy Campbell on 18 Jan 2017
Hey Matt,
The problem at play here is the fact that your test class definition itself (what happens the first time you load the class) is itself dependent on something that is not available. You are trying to make it available via the PathFixture, but that only gets applied at test run time, not test class loading time.
Have you considered deploying the source to in toolbox form? Doing so would allow you to perhaps install toolboxes at test runtime and all the path management would be handled by the toolbox packaging features. (see here and here for example).
If that does't work out, then you'll need to do something like the following:
classdef(...
SharedTestFixtures ={matlab.unittest.fixtures.PathFixture(fullfile(pwd,'source'))})...
MyClassTest < matlab.unittest.TestCase
properties( TestParameter )
enumName = getEnumNames
end
methods(Test, ParameterCombination = 'sequential' )
function SomeMethodTest( testCase, enumName )
thisEnum = MyClass.(enumName);
output = thisEnum.SomeMethod();
check = thisEnum.prop1 + thisEnum.prop2;
testCase.verifyEqual( output, check );
end
end
end
function names = getEnumNames
oldPath = path;
cl = onCleanup(@() path(oldPath));
addpath(fullfile(pwd,'source'));
names = cellstr(enumeration('MyClass'));
end
This ensures that the source code is on the path both at class load time (when the TestParameters are getting populated) and at test runtime (via the SharedTestFixture).
Hope that helps!
  2 Comments
Matt
Matt on 21 Jan 2017
Thanks for the help, Andy! I'm intrigued by the toolbox builder, but for the moment I'm going to see if your workaround use of a local function will help - the code I wrote about is part of a larger codebase that will require some coordination in order to package up properly. Thanks again!
-Matt
Andy Campbell
Andy Campbell on 24 Apr 2019
Note, now in 19a you can use a MATLAB project which will handle the path setup for you before laoding the test classes. More info here:

Sign in to comment.

More Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!