それを行うための準備機能はありません。これをハッキングする方法の 1 つを次に示します。
テストしている次の関数を考えてみましょう。数値以外の入力に対して特定のエラーをスローします。
増分.m
function out = increment(x)
if ~isa(x,'numeric')
error('increment:NonNumeric', 'Input must be numeric.');
end
out = x + 1;
end
ユニットテストコードは次のとおりです。
IncrementTest.m
classdef IncrementTest < matlab.unittest.TestCase
methods (Test)
function testOutput(t)
t.verifyEqual(increment(1), 2);
end
function testClass(t)
t.verifyClass(increment(1), class(2));
end
function testErrId(t)
t.verifyError(@()increment('1'), 'increment:NonNumeric');
end
function testErrIdMsg(t)
% expected exception
expectedME = MException('increment:NonNumeric', ...
'Input must be numeric.');
noErr = false;
try
[~] = increment('1');
noErr = true;
catch actualME
% verify correct exception was thrown
t.verifyEqual(actualME.identifier, expectedME.identifier, ...
'The function threw an exception with the wrong identifier.');
t.verifyEqual(actualME.message, expectedME.message, ...
'The function threw an exception with the wrong message.');
end
% verify an exception was thrown
t.verifyFalse(noErr, 'The function did not throw any exception.');
end
end
end
try/catch ブロックの使用は、 Steve Eddins によるassertExceptionThrown
古いxUnit テスト フレームワークの関数に触発されています。(更新: フレームワークは File Exchange から削除されたようです。代わりに新しい組み込みフレームワークを使用することをお勧めします。興味がある場合は、古い xUnit の人気のあるフォークを次に示します: psexton/matlab-xunit )。
エラー メッセージをテストする際にもう少し柔軟性を持たせたい場合は、verifyMatches
正規表現を使用して文字列と照合する代わりに を使用します。
また、冒険したい場合は、matlab.unittest.constraints.Throws
クラスを学習して、エラー ID に加えてエラー メッセージをチェックする独自のバージョンを作成することもできます。
ME = MException('error:id', 'message');
import matlab.unittest.constraints.Throws
%t.verifyThat(@myfcn, Throws(ME));
t.verifyThat(@myfcn, ThrowsWithId(ME));
拡張バージョンはどこThrowsWithId
ですか
編集:
わかりましたので、のコードを調べて、カスタムクラスmatlab.unittest.constraints.Throws
を実装しました。 Constraint
クラスは に似ていThrows
ます。インスタンスを入力として受け取りMException
、テスト対象の関数ハンドルが同様の例外をスローするかどうかをチェックします (エラー ID とメッセージの両方をチェックします)。どのアサーション メソッドでも使用できます。
testCase.assertThat(@fcn, ThrowsErr(ME))
testCase.assumeThat(@fcn, ThrowsErr(ME))
testCase.fatalAssertThat(@fcn, ThrowsErr(ME))
testCase.verifyThat(@fcn, ThrowsErr(ME))
抽象クラス からサブクラスを作成するmatlab.unittest.constraints.Constraint
には、インターフェースの 2 つの関数を実装する必要があります:satisfiedBy
とgetDiagnosticFor
. FunctionHandleConstraint
また、関数ハンドルを操作するためのいくつかのヘルパー メソッドを提供するため、代わりに別の抽象クラスから継承することにも注意してください。
コンストラクターは、予想される例外 (MException
インスタンスとして) と、テスト対象の関数ハンドルを呼び出すための出力引数の数を指定するオプションの入力を受け取ります。
コード:
classdef ThrowsErr < matlab.unittest.internal.constraints.FunctionHandleConstraint
%THROWSERR Constraint specifying a function handle that throws an MException
%
% See also: matlab.unittest.constraints.Throws
properties (SetAccess = private)
ExpectedException;
FcnNargout;
end
properties (Access = private)
ActualException = MException.empty;
end
methods
function constraint = ThrowsErr(exception, numargout)
narginchk(1,2);
if nargin < 2, numargout = 0; end
validateattributes(exception, {'MException'}, {'scalar'}, '', 'exception');
validateattributes(numargout, {'numeric'}, {'scalar', '>=',0, 'nonnegative', 'integer'}, '', 'numargout');
constraint.ExpectedException = exception;
constraint.FcnNargout = numargout;
end
end
%% overriden methods for Constraint class
methods
function tf = satisfiedBy(constraint, actual)
tf = false;
% check that we have a function handle
if ~constraint.isFunction(actual)
return
end
% execute function (remembering that its been called)
constraint.invoke(actual);
% check if it never threw an exception
if ~constraint.HasThrownAnException()
return
end
% check if it threw the wrong exception
if ~constraint.HasThrownExpectedException()
return
end
% if we made it here then we passed
tf = true;
end
function diag = getDiagnosticFor(constraint, actual)
% check that we have a function handle
if ~constraint.isFunction(actual)
diag = constraint.getDiagnosticFor@matlab.unittest.internal.constraints.FunctionHandleConstraint(actual);
return
end
% check if we need to execute function
if constraint.shouldInvoke(actual)
constraint.invoke(actual);
end
% check if it never threw an exception
if ~constraint.HasThrownAnException()
diag = constraint.FailingDiagnostic_NoException();
return
end
% check if it threw the wrong exception
if ~constraint.HasThrownExpectedException()
diag = constraint.FailingDiagnostic_WrongException();
return
end
% if we made it here then we passed
diag = PassingDiagnostic(constraint);
end
end
%% overriden methods for FunctionHandleConstraint class
methods (Hidden, Access = protected)
function invoke(constraint, fcn)
outputs = cell(1,constraint.FcnNargout);
try
[outputs{:}] = constraint.invoke@matlab.unittest.internal.constraints.FunctionHandleConstraint(fcn);
constraint.ActualException = MException.empty;
catch ex
constraint.ActualException = ex;
end
end
end
%% private helper functions
methods (Access = private)
function tf = HasThrownAnException(constraint)
tf = ~isempty(constraint.ActualException);
end
function tf = HasThrownExpectedException(constraint)
tf = metaclass(constraint.ActualException) <= metaclass(constraint.ExpectedException) && ...
strcmp(constraint.ActualException.identifier, constraint.ExpectedException.identifier) && ...
strcmp(constraint.ActualException.message, constraint.ExpectedException.message);
end
function diag = FailingDiagnostic_NoException(constraint)
import matlab.unittest.internal.diagnostics.ConstraintDiagnosticFactory;
import matlab.unittest.internal.diagnostics.DiagnosticSense;
subDiag = ConstraintDiagnosticFactory.generateFailingDiagnostic(...
constraint, DiagnosticSense.Positive);
subDiag.DisplayDescription = true;
subDiag.Description = 'The function did not throw any exception.';
subDiag.DisplayExpVal = true;
subDiag.ExpValHeader = 'Expected exception:';
subDiag.ExpVal = sprintf('id = ''%s''\nmsg = ''%s''', ...
constraint.ExpectedException.identifier, ...
constraint.ExpectedException.message);
diag = constraint.generateFailingFcnDiagnostic(DiagnosticSense.Positive);
diag.addCondition(subDiag);
end
function diag = FailingDiagnostic_WrongException(constraint)
import matlab.unittest.internal.diagnostics.ConstraintDiagnosticFactory;
import matlab.unittest.internal.diagnostics.DiagnosticSense;
if strcmp(constraint.ActualException.identifier, constraint.ExpectedException.identifier)
field = 'message';
else
field = 'identifier';
end
subDiag = ConstraintDiagnosticFactory.generateFailingDiagnostic(...
constraint, DiagnosticSense.Positive, ...
sprintf('''%s''',constraint.ActualException.(field)), ...
sprintf('''%s''',constraint.ExpectedException.(field)));
subDiag.DisplayDescription = true;
subDiag.Description = sprintf('The function threw an exception with the wrong %s.',field);
subDiag.DisplayActVal = true;
subDiag.DisplayExpVal = true;
subDiag.ActValHeader = sprintf('Actual %s:',field);
subDiag.ExpValHeader = sprintf('Expected %s:',field);
diag = constraint.generateFailingFcnDiagnostic(DiagnosticSense.Positive);
diag.addCondition(subDiag);
end
function diag = PassingDiagnostic(constraint)
import matlab.unittest.internal.diagnostics.ConstraintDiagnosticFactory;
import matlab.unittest.internal.diagnostics.DiagnosticSense;
subDiag = ConstraintDiagnosticFactory.generatePassingDiagnostic(...
constraint, DiagnosticSense.Positive);
subDiag.DisplayExpVal = true;
subDiag.ExpValHeader = 'Expected exception:';
subDiag.ExpVal = sprintf('id = ''%s''\nmsg = ''%s''', ...
constraint.ExpectedException.identifier, ...
constraint.ExpectedException.message);
diag = constraint.generatePassingFcnDiagnostic(DiagnosticSense.Positive);
diag.addCondition(subDiag);
end
end
end
使用例を次に示します (以前と同じ関数を使用):
t = matlab.unittest.TestCase.forInteractiveUse;
ME = MException('increment:NonNumeric', 'Input must be numeric.');
t.verifyThat(@()increment('5'), ThrowsErr(ME))
ME = MException('MATLAB:TooManyOutputs', 'Too many output arguments.');
t.verifyThat(@()increment(5), ThrowsErr(ME,2))
アップデート:
この回答を投稿してから、一部のクラスの内部が少し変更されました。最新の MATLAB R2016a で動作するように上記のコードを更新しました。古いバージョンが必要な場合は、改訂履歴を参照してください。