MATLAB unit testing a function call -
i have following simple utility function:
function vfprintf(verbose, varargin) % vfprintf display output optionally depending on level of verbosity. % % vfprintf(tf, args) passes arguments args built-in matlab % command |fprintf| if tf logical true. if tf logical false, vfprintf % nothing. assert(islogical(verbose),... 'utils:invalidverbose',... 'verbose must logical true or false'); if verbose fprintf(varargin{:}); end
it turns out though function super simple, had issue caused me problem (the assert
condition should have been islogical(verbose) && isscalar(verbose)
, not islogical(verbose)
), i'd implement unit tests around it.
note don't want test fprintf
- i'm assuming that's ok. there way can test like:
- "if
verbose
logical scalar true, call madefprintf
" - "if
verbose
logical scalar false, no call madefprintf
" - "if
verbose
logical nonscalar, no call madefprintf
" - "if
verbose
not logical, no call madefprintf
"
i can't find way verify call made out particular function. ideas? thing can think of mock out fprintf
own function shadows real 1 on matlab path, somehow raises fprintfcalled
event listened testing code tell when it's called. approach? seems overkill.
or perhaps i'm approaching wrong way - maybe should forget testing calls made, , instead testing command-line and/or file output of vfprintf
directly. feels i'm testing fprintf
rather vfprintf
.
maybe i'm overthinking things, i'd improve testing practices, appreciate advice. thanks!
i think @ point have 4 options. 4th, i'll run through them all:
- execute calls vfprintf within evalc validate printed command window or create file , print file. drawbacks 1: kind of tests fprintf (athough more academic since there low chance fprintf change or not honor contract). drawback 2: both cases interact global state - either global command window output (which other things can print to) or filesystem. not end of world, avoid if can. nicer test avoid touching outside world.
- shadow fprintf function. can putting in folder off path , adding own fprintf function in folder, using pathfixture in test add top of path. drawback: still relies on changing global state (the path) , may slower because path manipulation expensive language execution perspective. not big fan, here how might play out. if can i'd suggest going #4 below on this:
verboseargumentsholder.m
classdef verboseargumentsholder < handle properties arguments = {}; end end
verboseprinterspy.m
classdef verboseprinterspy properties(constant) argumentsholder = verboseargumentsholder; end end
* (test folder)/overloads/fprintf/fprintf.m*
function fprintf(varargin) argholder = verboseprinterspy.argumentsholder; argholder.arguments = varargin; end
vfprintftest.m
classdef vfprintftest < matlab.unittest.testcase methods(test) function testwhenscalartrue(testcase) import matlab.unittest.fixtures.pathfixture; testcase.applyfixture(pathfixture(... fullfile((test folder),'overloads','fprintf'))); argholder = verboseprinterspy.argumentsholder; argholder.arguments = {}; % reset values since global , stateful. vfprintf(true,'dummy input'); testcase.verifyequal(argholder.arguments, 'dummy input'); end function testwhenscalarfalse(testcase) testcase.applyfixture(pathfixture(... fullfile((test folder),'overloads','fprintf'))); argholder = verboseprinterspy.argumentsholder; argholder.arguments = {}; % reset values vfprintf(false,'dummy input'); testcase.verifyempty(argholder.arguments); end end end
restructure production code have interface printing, can add test specific spy interface. nice approach, has implication on software structure may not easy adjust , if codebase heavily reliant on utility.
since passing on varargin fprintf directly, can create test double fprintf method test out. then, fprintf call dispatch testing specific class can spy on inputs. may this:
verboseprinterspy.m
classdef verboseprinterspy < handle properties wasinvoked = false; argumentsusedinprintcall = {'not invoked'}; end methods function fprintf(spy, varargin) spy.wasinvoked = false; spy.argumentsusedinprintcall = varargin; end end end
vfprintftest.m
classdef vfprintftest < matlab.unittest.testcase methods(test) function testwhenscalartrue(testcase) spy = verboseprinterspy; vfprintf(true, spy, 'dummy input'); testcase.verifytrue(spy.wasinvoked); testcase.verifyequal(spy.argumentsusedinprintcall, 'dummy input'); end function testwhenscalarfalse(testcase) spy = verboseprinterspy; vfprintf(false, spy, 'dummy input'); testcase.verifyfalse(spy.wasinvoked); end end end
hope helps!
Comments
Post a Comment