This post discusses how to mock functions in MATLAB. The issue of testing code that depends on external data sources, such as an Excel file or a database, in MATLAB. When the input data from these sources changes, it can cause tests to fail even if the code itself is correct. The article presents a solution without using the Mocking Framework in MATLAB but adding the same function name to the path and call it accordingly.
Example
The example code provided consists of two functions: mainfunction
and readexcel
. The mainfunction
reads data from an Excel file using the readexcel
function and performs some operations on it. The goal is to test the mainfunction
without relying on the actual data in the Excel file.
function out = mainfunction() filename = "data.xlsx"; out = readexcel(filename); out.Age = out.Age*2;
function out = readexcel(filename) disp("This is the real function") out = readtable(filename);
The question that arises is how should one test mainfunction
if it depends on data.xlsx
which may or may not change.
A first approach could be run mainfunction.m
, store the output in a matfile (baselineTest.mat
) and then create a test that compares the matfile and the code (typically Regression Test).
classdef TestMainFunction < matlab.unittest.TestCase methods (Test) % Test methods function baselineTest(testCase) out = mainfunction(); exp = load("baselineTest.mat"); testCase.verifyEqual(out,exp.out) end end end
This works nicely when data.xlsx
is the same, but as soon as it changes your test fails without any code changes, making it hard to collaborate. This is where mock comes into play.
Mock
To overcome this issue, the post introduces the concept of mocking. Mocking allows replacing a function’s actual implementation with a dummy or mock version for testing purposes. However, the Mocking Framework in MATLAB primarily supports mocking classes and does not directly support mocking functions.
To mock functions in MATLAB the readexcel
function, the article suggests creating a second function with the same name (readexcel
) in a subfolder within the test folder called “mock.” This function serves as a dummy or mock implementation of readexcel
that does not depend on external data. It mimics the input/output behavior of the original readexcel
function, allowing solid testing of the mainfunction
.
function out = readexcel(filename) disp("This a mock of readexcel") Age = 1:10; Name = repmat("Gareth",10,1); out = table(Age',Name,'VariableNames',{'Age','Name'});
Now notice the difference in these two test methods baselineTest
and baselineTestMock
:
classdef TestMainFunction < matlab.unittest.TestCase methods (Test) % Test methods function baselineTest(testCase) out = mainfunction(); exp = load("baselineTest.mat"); testCase.verifyEqual(out,exp.out) end function baselineTestMock(testCase) import matlab.unittest.fixtures.PathFixture; testCase.applyFixture(PathFixture(fullfile(pwd,'mock'))); out = mainfunction(); testCase.verifyEqual(out.Age(1),2) end end end
The first test method will call readexcel
(as expected) but it depends on the data and this test is not robust to data file or database changes.
The second test method will call the dummy or mocked readexcel
which in turn does not depend on any external data, and it mimics the readexcel input/output so we can test the numerics in a solid way of the baseline.