Testing private functions in classes

조회 수: 80 (최근 30일)
JWTay
JWTay 2017년 10월 16일
편집: Martin Lechner 2020년 6월 2일
I'm wondering what the best way is to write unit tests for private functions and properties in classes. Say for example, I have a class that represents a ball launched as a projectile:
class ball
%
properties (Access = private)
initialSpeed = 25; %m/s
acceleration = -10; %m/s^2
end
%
methods
function distTravelled = getDistanceTravelled(obj, timeElapsed)
% Estimate total distance traveled by the ball
% (I know this is a bad approximation)
currSpeed = obj.getCurrSpeed(timeElapsed);
distTravelled = 0.5 * (obj.initialSpeed + currSpeed) * timeElapsed;
end
end
%
methods (Access = private)
function currSpeed = getCurrSpeed(obj, timeElapsed)
%Calculate current speed of ball
currSpeed = obj.initialSpeed + obj.acceleration * timeElapsed;
end
end
end
How should I write a test to check that the values for acceleration or that the value returned by the method getCurrSpeed is accurate? Should I just allow access to the testing functions?

채택된 답변

per isakson
per isakson 2017년 10월 19일
편집: per isakson 2017년 10월 20일
First goggle "test private method" and read about why you should not do it (and a few ways to do it).
One way (for handle classes only) is to include the test in the class itself.
>> mc = MyClass
mc =
MyClass with no properties.
>> mc.test_private
Private mysort is running
>>
where
classdef MyClass < matlab.unittest.TestCase
%
methods ( Test )
function test_private( this )
this.mysort
end
end
methods ( Access = private )
function mysort( this )
fprintf( 'Private mysort is running\n' )
end
end
end
A better workaround based on localfunctions (added 19 hours later)
>> mc = MyClass;
>> fh = mc.get_local_function_handle;
>> fh = fh{1}
fh =
@the_tricky_algorithm_
>> fh( mc )
ans =
144
>>
where in one mfile
classdef MyClass
properties
val = 12;
end
methods
function fh = get_local_function_handle( ~ )
fh = localfunctions();
end
end
methods ( Access = private )
function my_private_method( this )
fprintf( 'Private mysort is running\n' )
the_tricky_algorithm_( this )
end
end
end
function out = the_tricky_algorithm_( this )
out = this.val .* this.val;
end
  댓글 수: 3
Walter Roberson
Walter Roberson 2017년 10월 20일
When I worked in industry, we tested everything.
Later I worked with some people developing medical devices; I'm pretty sure their applications would have been rejected if they didn't test everything.
JWTay
JWTay 2017년 10월 20일
Good to know! I am trying to develop a "best practices" in regards to my coding so I appreciate the info.

댓글을 달려면 로그인하십시오.

추가 답변 (4개)

Wil Koenen
Wil Koenen 2018년 7월 3일
If you're using matlab.unittest.TestCase for your test, but you don't want matlab.unittest.TestCase to be a superclass of the class under test, you can grant access to private functions by providing a list of classes instead of the private keyword. Example:
classdef classUnderTest
...
methods ( Access = { ?classUnderTest, ?matlab.unittest.TestCase } )
...
end
end
  댓글 수: 2
Tom Hawkins
Tom Hawkins 2020년 2월 12일
Thanks, that's a great tip!
Martin Lechner
Martin Lechner 2020년 6월 2일
편집: Martin Lechner 2020년 6월 2일
This solution works perfect. Thanks.
I enabled explictly my test class but this included my unit test class in the compiler output. Your solution to enable access for all matlab.unittest.TestCase doesn't include the test classes in the compiler output.

댓글을 달려면 로그인하십시오.


Sean de Wolski
Sean de Wolski 2017년 10월 19일
편집: Sean de Wolski 2017년 10월 19일
I would test it through the front door with a known set of inputs and expected outputs. This is the beautiful thing about classes in that you can mask the implementation from the outside world and change it later if necessary.
e.g.:
In Unit Test
b = ball
distTravelled = getDistanceTravelled(b, 20);
testCase.verifyEqual(distTravelled, whatever_is_right_for20);
distTravelled = getDistanceTravelled(b, 200);
testCase.verifyEqual(distTravelled, whatever_is_right_for200);
testCase.verifyError(@()getDistanceTravelled(b, -2), 'ball:NoNegativeTime');
You can use the Code Coverage Plugin for a test runner to make sure that you're exciting all of the lines of the implementation and that should be fine.
  댓글 수: 1
JWTay
JWTay 2017년 10월 20일
That is my practice in general.
I guess that the reason I wanted to test some of the private functions is to make sure that the calculations are correct, and this is not directly reflected in the output (i.e. the results of the private methods are used to influence some decision-making in the code). I could come up with model input/expected output data but when it breaks, it might not be easy to trace which function was giving the wrong answers.

댓글을 달려면 로그인하십시오.


Sanjana Ramakrishnan
Sanjana Ramakrishnan 2017년 10월 19일
편집: per isakson 2017년 10월 19일
Refer the below link for an example of writing MATLAB unit tests relevant to your case:
Please note that testing private functions is just the same as testing any other function in a class.
You can design your own testing strategies as per your requirement.
  댓글 수: 1
per isakson
per isakson 2017년 10월 19일
"testing private functions is just the same as testing any other function in a class" Is that really so?

댓글을 달려면 로그인하십시오.


Tom Hawkins
Tom Hawkins 2020년 2월 12일
Would you be happy to define your properties/methods as Protected, rather than Private? If so you could then create a subclass of your class as part of your test suite, for example:
classdef BallWithAccessToProtected < ball
methods
function obj = BallWithAccessToProtected(varargin)
obj@ball(varargin{:})
end
function SetProtectedProperty(obj, prop, value)
obj.(prop) = value;
end
function v = GetProtectedProperty(obj, prop)
v = obj.(prop);
end
end
end
This subclass exposes the protected properties via its SetProtectedProperty and GetProtectedProperty methods, for example:
>> b = BallWithAccessToProtected(someArgs);
>> b.GetProtectedProperty('initialSpeed')
ans =
25
You can figure out how to extend that to accessing methods if you need to.
Protected means that users of your class's 'official' API can't access those properties and methods, but someone who's willing to subclass it can (as we've done above) - but remember that some languages like Python don't even have a mechanism for restricting access to internal properties and methods of a class, only a naming convention to show which ones you shouldn't really use.
  댓글 수: 1
Wil Koenen
Wil Koenen 2020년 2월 13일
In your example, you could leave out the constructor, because MATLAB supplies a default constructor that does the same (reference).
When a subclass does not define a constructor, the default constructor passes its inputs to the direct superclass constructor. This behavior is useful when there is no need for a subclass to define a constructor, but the superclass constructor does require input arguments.

댓글을 달려면 로그인하십시오.

카테고리

Help CenterFile Exchange에서 Construct and Work with Object Arrays에 대해 자세히 알아보기

Community Treasure Hunt

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

Start Hunting!

Translated by