Modellierung und Simulation - Modultest: Unterschied zwischen den Versionen

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen
 
(36 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 99: Zeile 99:
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestQuadrat < matlab.unittest.TestCase
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestQuadrat < matlab.unittest.TestCase
     methods(Test)
     methods(Test)
         function testPositive(testCase)
         function testPositiv(testCase)
             actual = quadrat(4);
             testCase.verifyEqual(quadrat(3),9);
             expected = 16;
        end
 
        function testNegativ(testCase)
             testCase.verifyEqual(actual,expected);
             testCase.verifyEqual(quadrat(-4),16);
        end
        function testNull(testCase)
             testCase.verifyEqual(quadrat(0),0);
         end
         end
     end
     end
end
end
</syntaxhighlight>
</syntaxhighlight>
== Test ausführen ==
results = runtests('TestQuadrat');
'''Ausgabe:'''
Running TestQuadrat
.
Done TestQuadrat
=5. Toleranzen bei Fließkommazahlen=
=5. Toleranzen bei Fließkommazahlen=
Direkte Vergleiche sind problematisch:
Direkte Vergleiche sind problematisch:
Zeile 113: Zeile 124:
Daher
Daher
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">actual = sin(pi);
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">actual = sin(pi);
testCase.verifyEqual( ...
testCase.verifyEqual(actual, 0, 'AbsTol',1e-10);
    actual,...
    0,...
    'AbsTol',1e-10);
</syntaxhighlight>
</syntaxhighlight>


Zeile 123: Zeile 131:
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function y = wurzel(x)
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function y = wurzel(x)
     if x < 0
     if x < 0
         error('MATLAB:NegativeInput',...
         error('MATLAB:NegativeInput','Nur positive Werte erlaubt')
              'Nur positive Werte erlaubt')
     end
     end
     y = sqrt(x);
     y = sqrt(x);
Zeile 132: Zeile 139:
== Test ==
== Test ==
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function testError(testCase)
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function testError(testCase)
     testCase.verifyError( ...
     testCase.verifyError( ...
         @() wurzel(-5), ...
         @() wurzel(-5), ...
         'MATLAB:NegativeInput');
         'MATLAB:NegativeInput');
end
end
</syntaxhighlight>
</syntaxhighlight>


= 7. Parameterisierte Tests=
= 7. Parameterisierte Tests=
Oft möchte man dieselbe Funktion mit vielen verschiedenen Eingabewerten testen. Ohne parameterisierte Tests müsste man für jeden Fall eine eigene Testmethode schreiben.
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>TestQuadrat ohne Parameterisierung&thinsp;</strong>
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestQuadrat < matlab.unittest.TestCase
    methods(Test)
        function test0(testCase)
            testCase.verifyEqual(quadrat(0),0);
        end
        function test1(testCase)
            testCase.verifyEqual(quadrat(1),1);
        end
        function test2(testCase)
            testCase.verifyEqual(quadrat(2),4);
        end
        function test3(testCase)
            testCase.verifyEqual(quadrat(3),9);
        end
    end
end
</syntaxhighlight>
|}
Das funktioniert, ist aber:
* viel Code
* schlecht wartbar
* fehleranfällig
Die elegantere Lösung besteht darin die Testdaten als Eigenschaften zu definieren:
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestQuadrat < matlab.unittest.TestCase
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestQuadrat < matlab.unittest.TestCase
     properties(TestParameter)
     properties(TestParameter)
Zeile 147: Zeile 186:
     end
     end


     methods(Test)
     methods(Test,ParameterCombination = "sequential") % Parameter werden sequentiell verglichen 0-0, 1-1, 2-4, 3-9
         function testQuadrat(testCase,x,y)
         function testQuadrat(testCase,x,y)
             testCase.verifyEqual(quadrat(x),y);
             testCase.verifyEqual(quadrat(x),y);
Zeile 154: Zeile 193:
end
end
</syntaxhighlight>
</syntaxhighlight>
== Testausführung ==
results = runtests('TestQuadrat');
'''
Ausgabe:'''
Running TestQuadrat
....
Done TestQuadrat


= 8. Prüfvorrichtungen =
= 8. Testumgebung =
Vorbereitung gemeinsamer Ressourcen.
Jeder Test soll unter definierten und reproduzierbaren Bedingungen starten.
 
Typische Vorbereitungen sind:
* Testdaten laden
* Dateien erzeugen
* Verzeichnisse anlegen
* Hardware initialisieren
* Parameter setzen
* Objekte erzeugen
 
Ohne Fixtures müsste derselbe Vorbereitungscode in jeder Testmethode wiederholt werden.


== Beispiel ==
== Beispiel ==
Angenommen mehrere Tests benötigen dieselbe Abtastrate
fs = 1000; % in Hz
In der Funktion <code>setup</code> wird die Abtastrate definiert.
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">methods(TestMethodSetup)
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">methods(TestMethodSetup)


Zeile 166: Zeile 225:
end
end
</syntaxhighlight>
</syntaxhighlight>
== Testmethode ==
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestFilter < matlab.unittest.TestCase
    methods(TestMethodSetup)
        function setup(testCase)
            testCase.TestData.fs = 1000;
        end
    end
    methods(Test)
        function testLength(testCase)
            fs = testCase.TestData.fs;
        end
        function testAmplitude(testCase)
            fs = testCase.TestData.fs;
        end
    end
end
</syntaxhighlight>
== Anwendung ==
== Anwendung ==
  fs = testCase.TestData.fs;
  fs = testCase.TestData.fs;
Zeile 174: Zeile 254:


= 9. Beispiel aus der Signalverarbeitung =
= 9. Beispiel aus der Signalverarbeitung =
Zu testdende Funktion
Zu testende Funktion
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function y = movingAverage(x)
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function y = movingAverage(x)
     y = movmean(x,3);
     y = movmean(x,3);
Zeile 186: Zeile 266:
             x = ones(1,100);
             x = ones(1,100);
             y = movingAverage(x);
             y = movingAverage(x);
             testCase.verifyEqual( ...
             testCase.verifyEqual(y,x,'AbsTol',1e-12);
                y,...
                x,...
                'AbsTol',1e-12);
         end
         end
     end
     end
Zeile 196: Zeile 273:


= 10. Testbericht =
= 10. Testbericht =
Ein Testbericht ist die strukturierte Ausgabe aller Testergebnisse nach der Ausführung einer Testsuite. Er zeigt nicht nur, ob Tests bestanden wurden, sondern auch welche Tests, warum, wie lange und mit welchem Ergebnis.
In MATLAB erfolgt das über das Unit-Test-Framework MATLAB mit dem TestRunner.
Wenn Sie Tests ausführst, willen Sie typischerweise mehr als nur:
OK / FAILED
Ein Testbericht beantwortet z.&thinsp;B.:
* Welche Tests wurden ausgeführt?
* Welche sind bestanden/fehlgeschlagen?
* Welche Fehler sind aufgetreten?
* Wie lange hat jeder Test gedauert?
* Gab es Warnungen oder Abbrüche?
== Standard-Testausführung ==
  suite = testsuite(pwd);
  suite = testsuite(pwd);
results = runtests(suite);
Tipp: <code>pwd</code> ist eine eingebaute MATLAB-Funktion und steht für <code>Print Working Directory</code>. <code>pwd</code> gibt den aktuellen Arbeitsordner zurück, also den Ordner, in dem MATLAB gerade arbeitet.
== TestRunner für detaillierte Berichte ==
Der zentrale Baustein ist der TestRunner
  runner = matlab.unittest.TestRunner.withTextOutput;
  runner = matlab.unittest.TestRunner.withTextOutput;
  results = runner.run(suite);
  results = runner.run(suite);
* <code>TestRunner</code> führt die Tests kontrolliert aus
* <code>withTextOutput</code> erzeugt eine strukturierte Textausgabe
* <code>results</code> enthält alle Testergebnisse als Objektliste


Auswertung:
Auswertung:
Zeile 205: Zeile 306:
* Incomplete
* Incomplete
* Duration
* Duration
== Beispiel ==
Zu testende Funktion
function y = addiere(a,b)
    y = a + b;
end
=== Testklasse ===
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestAddiere < matlab.unittest.TestCase
    methods(Test)
        function testPositive(testCase)
            testCase.verifyEqual(addiere(2,3),5);
        end
        function testNegative(testCase)
            testCase.verifyEqual(addiere(-2,-3),-5);
        end
        function testMixed(testCase)
            testCase.verifyEqual(addiere(-2,5),3);
        end
    end
end
</syntaxhighlight>
=== Standard-Testausführung===
results = runtests('TestAddiere');
Typische Ausgabe im Command Window
Running TestAddiere
...
Done TestAddiere
* Jeder Punkt . = Test bestanden
* F = Failed
* E = Error
===Ausgabe als Ergebnisobjekte===
results =
  1×3 TestResult array with properties:
    Name
    Passed
    Failed
    Incomplete
    Duration
    Details
Totals:
  3 Passed, 0 Failed, 0 Incomplete.
  0.038418 seconds testing time.
=== äquivalente Alternative ===
suite = testsuite('TestAddiere');
results = run(suite);
=== TestRunner mit Textausgabe ===
Das ist ein „professioneller Runner“, der einen strukturierten Bericht erzeugt.
import matlab.unittest.TestRunner
runner = matlab.unittest.TestRunner.withTextOutput('Verbosity', matlab.unittest.Verbosity.Detailed);
'''Test ausführen'''
results = runner.run(suite)
'''Beispielausgabe (detaillierter Bericht)'''
Running TestAddiere
  Setting up TestAddiere
  Done setting up TestAddiere in 0 seconds
  Running TestAddiere/testPositive
  Done TestAddiere/testPositive in 0.0023986 seconds
  Running TestAddiere/testNegative
  Done TestAddiere/testNegative in 0.0016029 seconds
  Running TestAddiere/testMixed
  Done TestAddiere/testMixed in 0.0016074 seconds
  Tearing down TestAddiere
  Done tearing down TestAddiere in 0 seconds
Done TestAddiere in 0.0056089 seconds
== Test-Suite sichern und laden ==
Test-Suite sichern
suite = testsuite(pwd);
save suite.mat suite
Test-Suite laden
load suite.mat
results = run(suite);
== Test Browser ==
[[Datei:MATLABTestBrowser.png|thumb|rigth|300px|Abb. 1: MATLAB Test Browser]]
Der MATLAB Test Browser ist eine grafische Oberfläche des MATLAB Unit Test Frameworks, mit der sich automatisierte Tests komfortabel verwalten und ausführen lassen. Er bietet einen schnellen Überblick über vorhandene Testdateien, Testklassen und Testmethoden und ermöglicht die gezielte Auswahl einzelner oder mehrerer Tests. Die Ergebnisse werden übersichtlich dargestellt, sodass erfolgreiche, fehlgeschlagene oder übersprungene Tests leicht erkannt und analysiert werden können. Dadurch unterstützt der Test Browser Entwickler dabei, die Qualität und Zuverlässigkeit von MATLAB-Code sicherzustellen und Fehler frühzeitig zu erkennen, ohne Tests ausschließlich über die Kommandozeile ausführen zu müssen.
Sie finden ihn im Panel ab R2025a in dem auch der Copilot Chat zu finden ist.


= Praktische Übung =
= Praktische Übung =
Zeile 214: Zeile 399:
Erstellen Sie mindestens drei Unit-Tests.
Erstellen Sie mindestens drei Unit-Tests.
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>Musterlösung 11.1&thinsp;</strong>
| <strong>Testklasse TestCube.m&thinsp;</strong>
|-
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestMovingAverage ...
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestCube ...
         < matlab.unittest.TestCase
         < matlab.unittest.TestCase
    methods(Test)
        function testConstantSignal(testCase)
            x = -5:5;
            y = cube(x);
            testCase.verifyEqual(y,x^3);
        end
    end
end
</syntaxhighlight>
|}


== Aufgabe 11.2 ==
Entwickeln Sie Tests für:
function y = celsius2fahrenheit(x)
Prüfen Sie:
* 0 °C
* 100 °C
* −40 °C
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>celsius2fahrenheit.m&thinsp;</strong>
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function F = celsius2fahrenheit(C)
    F = C*9/5 + 32;
end
</syntaxhighlight>
|}
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>Testklasse TestCelsius2Fahrenheit.m&thinsp;</strong>
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestCelsius2Fahrenheit < matlab.unittest.TestCase
    properties(TestParameter)
        C = {-40,0,100};
        F = {-40,32,212};
    end
    methods(Test)
        function testConversion(testCase,C,F)
            testCase.verifyEqual( ...
                celsius2fahrenheit(C), ...
                F);
        end
    end
end
</syntaxhighlight>
|}
== Aufgabe 11.3 – Butterworth-Tiefpassfilter==
Ein Butterworth-Tiefpassfilter 2. Ordnung soll getestet werden.
Entwickeln Sie einen Test, der nachweist:
* Ein konstantes Signal bleibt konstant.
* Die Ausgangslänge entspricht der Eingangslänge.
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>TiefpassFilter.m&thinsp;</strong>
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function y = TiefpassFilter(x)
    fs = 100;
    fc = 5;
    n  = 2;
    [b,a] = butter(n,fc/(fs/2));
    y = filter(b,a,x);
end
</syntaxhighlight>
|}
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>Testklasse TestTiefpassFilter &thinsp;</strong>
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestTiefpassFilter < matlab.unittest.TestCase
     methods(Test)
     methods(Test)
        function testLength(testCase)
            x = rand(1,1000);
            y = lowpassFilter(x);
            testCase.verifyEqual( ...
                length(y), ...
                length(x));
        end


         function testConstantSignal(testCase)
         function testConstantSignal(testCase)
            x = ones(1,1000);
            y = lowpassFilter(x);
            testCase.verifyEqual(y(end),1,'AbsTol',1e-2);
        end
    end
end
</syntaxhighlight>
|}


             x = ones(1,100);
== Aufgabe 11.4 – Testklasse entwickeln==
Gegeben:
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function y = addiere(a,b)
    y = a+b;
end
</syntaxhighlight>
Erstellen Sie eine vollständige Testklasse mit mindestens vier Testfällen.
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>Testklasse TestAddierer.m&thinsp;</strong>
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestAddiere < matlab.unittest.TestCase
    methods(Test)
        function testPositive(testCase)
             testCase.verifyEqual(addiere(3,4), 7);
        end


             y = movingAverage(x);
        function testNegative(testCase)
             testCase.verifyEqual(addiere(-2,-3), -5);
        end


             testCase.verifyEqual( ...
        function testZero(testCase)
                y,...
             testCase.verifyEqual(addiere(0,0), 0);
                x,...
        end
                'AbsTol',1e-12);


        function testMixed(testCase)
            testCase.verifyEqual(addiere(-2,5),3);
         end
         end
    end
end
</syntaxhighlight>
|}


== Aufgabe 11.5 – Fehlerbehandlung testen==
Für die Funktion
function y = wurzel(x)
soll ein Fehler bei negativen Werten ausgelöst werden.
#Implementieren Sie die Funktion.
#Schreiben Sie den entsprechenden Unit-Test.
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>Funktion wurzel.m&thinsp;</strong>
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function y = wurzel(x)
    if x < 0
        error('MATLAB:NegativeInput', ...
            'Nur positive Werte erlaubt');
     end
     end
    y = sqrt(x);
end
</syntaxhighlight>
|}


{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>Testklasse TestWurzel.m&thinsp;</strong>
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestWurzel < matlab.unittest.TestCase
    methods(Test)
        function testPositive(testCase)
            testCase.verifyEqual(wurzel(9),3);
        end
        function testError(testCase)
            testCase.verifyError( ...
                @() wurzel(-5), ...
                'MATLAB:NegativeInput');
        end
    end
end
end
</syntaxhighlight>
</syntaxhighlight>
|}
|}


== Aufgabe 2 ==
== Aufgabe 11.6 – Signalverarbeitung==
Entwickeln Sie Tests für:
Eine Filterfunktion soll getestet werden.
function y = celsius2fahrenheit(x)
 
Prüfen Sie:
Prüfen Sie:
* 0 °C
*Ausgangslänge
* 100 °C
*Konstantes Eingangssignal
* −40 °C
*Keine NaN-Werte im Ergebnis
 
Implementieren Sie die Tests mit <code>verifyEqual</code>, <code>verifyTrue</code> und <code>verifyFalse</code>.
 
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>Funktion Butterworth.m&thinsp;</strong>
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">function y = Butterworth(x)
    [b,a] = butter(2,0.1);
    y = filter(b,a,x);
end
</syntaxhighlight>
|}
 
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>Testklasse TestButterworth.m&thinsp;</strong>
|-
| <syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef TestButterworth < matlab.unittest.TestCase
    methods(Test)
        function testOutputLength(testCase)
            x = rand(1,1000);
            y = myFilter(x);
            testCase.verifyEqual(length(y),length(x));
        end
 
        function testConstantSignal(testCase)
            x = ones(1,1000);
            y = myFilter(x);
            testCase.verifyEqual(y(end),1,'AbsTol',1e-2);
        end
 
        function testNoNaN(testCase)
            x = rand(1,1000);
            y = Butterworth(x);
            testCase.verifyFalse(any(isnan(y)));
        end
    end
end
</syntaxhighlight>
|}
 
= Fazit =
Der Modultest in MATLAB dient dazu, die korrekte Funktion von Programmen systematisch und automatisiert zu überprüfen. Im Zentrum steht das Unit-Test-Framework von MATLAB.
 
Für wissenschaftliche Algorithmen, Signalverarbeitung und Simulink-Modelle ist MATLAB mit seinem Unit-Test-Framework besonders geeignet.
 
Vorteile
* frühzeitige Fehlererkennung
* reproduzierbare Ergebnisse
* sichere Weiterentwicklung
* Dokumentation des Soll-Verhaltens
* Grundlage für kontinuierliche Integration
 


== Aufgabe 3 ==
== Aufgabe 4 ==
----
----
→ [[MATLAB-Befehle| MATLAB<sup>®</sup> Befehlsübersicht]]<br>
→ [[MATLAB-Befehle| MATLAB<sup>®</sup> Befehlsübersicht]]<br>
→ zurück zum Hauptartikel: [[BSE Modellierung und Simulation - SoSe26]]
→ zurück zum Hauptartikel: [[BSE Modellierung und Simulation - SoSe26]]

Aktuelle Version vom 26. Juni 2026, 07:32 Uhr

Autor: Prof. Dr.-Ing. Schneider
Lektion: 11
Termin: 26.06.2026

Einleitung

Modultests bilden die unterste Ebene des Testprozesses und dienen dazu, einzelne Softwareeinheiten frühzeitig und isoliert auf ihre korrekte Funktion zu überprüfen. Ziel ist es, Fehler bereits in einem sehr frühen Entwicklungsstadium zu erkennen, um spätere Integrationsprobleme zu vermeiden und die Gesamtqualität des Systems nachhaltig zu erhöhen. Dabei wird jedes Modul unabhängig von anderen Komponenten getestet, wobei sowohl funktionale Anforderungen als auch typische Fehlerszenarien betrachtet werden. Durch klar definierte Testfälle, reproduzierbare Testumgebungen und objektive Bewertungskriterien stellen Modultests sicher, dass die implementierten Funktionen robust, zuverlässig und spezifikationskonform sind.

Lernziele

Nach dieser Lektion können Sie

  • die Bedeutung von Unit-Tests erklären,
  • MATLAB-Testklassen erstellen,
  • Tests mit dem matlab.unittest Framework durchführen,
  • Test Fixtures verwenden,
  • Fehlerfälle gezielt prüfen,
  • Testberichte interpretieren,
  • Unit-Tests in Entwicklungsprozesse integrieren.

Vorbereitung

  1. Studieren Sie das Video Erste Schritte mit dem MATLAB Unit Test Framework.
  2. Weitere Informationen finden Sie im Artikel Ways to Write Unit Tests.
  3. Besuchen Sie den Online-Kurs Unit Testing
  4. Lassen Sie sich vom Copilot helfen einen Test zu generieren.

1. Motivation

Bei wissenschaftlicher Software entstehen häufig Fehler durch:

  • Änderungen am Algorithmus
  • Erweiterungen bestehender Funktionen
  • Refactoring
  • Teamarbeit

Unit-Tests stellen sicher, dass eine Funktion auch nach Änderungen weiterhin korrekt arbeitet.

Beispiel

Gegeben sei die Funktion:

function y = celsius2fahrenheit(x)
    y = x*9/5 + 32;
end

Frage: Wie kann sichergestellt werden, dass zukünftige Änderungen keine Fehler verursachen?

Antwort: → Automatisierte Unit-Tests.

2. Das MATLAB Unit-Test Framework

MATLAB stellt das Paket

matlab.unittest

bereit.

Tabelle 1: Bestandteile des MATLAB Unit-Test Framework
Element Zweck
TestCase Basisklasse für Tests
verifyEqual Ergebnis prüfen
verifyTrue Wahrheitswert prüfen
verifyError Fehler erwarten
TestSuite Testsammlung
TestRunner Testausführung

3. Erster Test

Zu testende Funktion

function y = quadrat(x)
    y = x.^2;
end

Testklasse

classdef TestQuadrat < matlab.unittest.TestCase
    methods(Test)
        function testPositive(testCase)
            actual = quadrat(4);
            expected = 16;

            testCase.verifyEqual(actual,expected);
        end
    end
end

Test ausführen

results = runtests('TestQuadrat');

Ausgabe:

Running TestQuadrat
.
Done TestQuadrat


4. Mehrere Testfälle

classdef TestQuadrat < matlab.unittest.TestCase
    methods(Test)
        function testPositiv(testCase)
            testCase.verifyEqual(quadrat(3),9);
        end
        function testNegativ(testCase)
            testCase.verifyEqual(quadrat(-4),16);
        end
        function testNull(testCase)
            testCase.verifyEqual(quadrat(0),0);
        end
    end
end

Test ausführen

results = runtests('TestQuadrat');

Ausgabe:

Running TestQuadrat
.
Done TestQuadrat

5. Toleranzen bei Fließkommazahlen

Direkte Vergleiche sind problematisch:

0.1 + 0.2 == 0.3

Daher

actual = sin(pi);
testCase.verifyEqual(actual, 0, 'AbsTol',1e-10);

6. Fehler testen

Funktion

function y = wurzel(x)
    if x < 0
        error('MATLAB:NegativeInput','Nur positive Werte erlaubt')
    end
    y = sqrt(x);
end

Test

function testError(testCase)
    testCase.verifyError( ...
        @() wurzel(-5), ...
        'MATLAB:NegativeInput');
end

7. Parameterisierte Tests

Oft möchte man dieselbe Funktion mit vielen verschiedenen Eingabewerten testen. Ohne parameterisierte Tests müsste man für jeden Fall eine eigene Testmethode schreiben.

Das funktioniert, ist aber:

  • viel Code
  • schlecht wartbar
  • fehleranfällig

Die elegantere Lösung besteht darin die Testdaten als Eigenschaften zu definieren:

classdef TestQuadrat < matlab.unittest.TestCase
    properties(TestParameter)
        x = {0,1,2,3};
        y = {0,1,4,9};
    end

    methods(Test,ParameterCombination = "sequential") % Parameter werden sequentiell verglichen 0-0, 1-1, 2-4, 3-9
        function testQuadrat(testCase,x,y)
            testCase.verifyEqual(quadrat(x),y);
        end
    end
end

Testausführung

results = runtests('TestQuadrat');

Ausgabe:

Running TestQuadrat
....
Done TestQuadrat

8. Testumgebung

Jeder Test soll unter definierten und reproduzierbaren Bedingungen starten.

Typische Vorbereitungen sind:

  • Testdaten laden
  • Dateien erzeugen
  • Verzeichnisse anlegen
  • Hardware initialisieren
  • Parameter setzen
  • Objekte erzeugen

Ohne Fixtures müsste derselbe Vorbereitungscode in jeder Testmethode wiederholt werden.

Beispiel

Angenommen mehrere Tests benötigen dieselbe Abtastrate

fs = 1000; % in Hz

In der Funktion setup wird die Abtastrate definiert.

methods(TestMethodSetup)

    function setup(testCase)
        testCase.TestData.fs = 1000;
    end
end

Testmethode

classdef TestFilter < matlab.unittest.TestCase
    methods(TestMethodSetup)
        function setup(testCase)
            testCase.TestData.fs = 1000;
        end
    end

    methods(Test)
        function testLength(testCase)
            fs = testCase.TestData.fs;
        end

        function testAmplitude(testCase)
            fs = testCase.TestData.fs;
        end
    end
end

Anwendung

fs = testCase.TestData.fs;

Typische Anwendungen:

  • Messdaten laden
  • Simulationsumgebungen vorbereiten
  • Testverzeichnisse anlegen

9. Beispiel aus der Signalverarbeitung

Zu testende Funktion

function y = movingAverage(x)
    y = movmean(x,3);
end

Test

classdef TestMovingAverage ...
        < matlab.unittest.TestCase
    methods(Test)
        function testConstantSignal(testCase)
            x = ones(1,100);
            y = movingAverage(x);
            testCase.verifyEqual(y,x,'AbsTol',1e-12);
        end
    end
end

10. Testbericht

Ein Testbericht ist die strukturierte Ausgabe aller Testergebnisse nach der Ausführung einer Testsuite. Er zeigt nicht nur, ob Tests bestanden wurden, sondern auch welche Tests, warum, wie lange und mit welchem Ergebnis.

In MATLAB erfolgt das über das Unit-Test-Framework MATLAB mit dem TestRunner.

Wenn Sie Tests ausführst, willen Sie typischerweise mehr als nur:

OK / FAILED

Ein Testbericht beantwortet z. B.:

  • Welche Tests wurden ausgeführt?
  • Welche sind bestanden/fehlgeschlagen?
  • Welche Fehler sind aufgetreten?
  • Wie lange hat jeder Test gedauert?
  • Gab es Warnungen oder Abbrüche?

Standard-Testausführung

suite = testsuite(pwd);
results = runtests(suite);

Tipp: pwd ist eine eingebaute MATLAB-Funktion und steht für Print Working Directory. pwd gibt den aktuellen Arbeitsordner zurück, also den Ordner, in dem MATLAB gerade arbeitet.

TestRunner für detaillierte Berichte

Der zentrale Baustein ist der TestRunner

runner = matlab.unittest.TestRunner.withTextOutput;
results = runner.run(suite);
  • TestRunner führt die Tests kontrolliert aus
  • withTextOutput erzeugt eine strukturierte Textausgabe
  • results enthält alle Testergebnisse als Objektliste

Auswertung:

  • Passed
  • Failed
  • Incomplete
  • Duration

Beispiel

Zu testende Funktion

function y = addiere(a,b)
   y = a + b;
end

Testklasse

classdef TestAddiere < matlab.unittest.TestCase
    methods(Test)
        function testPositive(testCase)
            testCase.verifyEqual(addiere(2,3),5);
        end
        function testNegative(testCase)
            testCase.verifyEqual(addiere(-2,-3),-5);
        end
        function testMixed(testCase)
            testCase.verifyEqual(addiere(-2,5),3);
        end
    end
end

Standard-Testausführung

results = runtests('TestAddiere');

Typische Ausgabe im Command Window

Running TestAddiere
...
Done TestAddiere
  • Jeder Punkt . = Test bestanden
  • F = Failed
  • E = Error

Ausgabe als Ergebnisobjekte

results =
 1×3 TestResult array with properties:
   Name
   Passed
   Failed
   Incomplete
   Duration
   Details
Totals:
  3 Passed, 0 Failed, 0 Incomplete.
  0.038418 seconds testing time.

äquivalente Alternative

suite = testsuite('TestAddiere');
results = run(suite);

TestRunner mit Textausgabe

Das ist ein „professioneller Runner“, der einen strukturierten Bericht erzeugt.

import matlab.unittest.TestRunner 
runner = matlab.unittest.TestRunner.withTextOutput('Verbosity', matlab.unittest.Verbosity.Detailed);

Test ausführen

results = runner.run(suite)

Beispielausgabe (detaillierter Bericht)

Running TestAddiere
 Setting up TestAddiere
 Done setting up TestAddiere in 0 seconds
  Running TestAddiere/testPositive
  Done TestAddiere/testPositive in 0.0023986 seconds
  Running TestAddiere/testNegative
  Done TestAddiere/testNegative in 0.0016029 seconds
  Running TestAddiere/testMixed
  Done TestAddiere/testMixed in 0.0016074 seconds
 Tearing down TestAddiere
 Done tearing down TestAddiere in 0 seconds
Done TestAddiere in 0.0056089 seconds

Test-Suite sichern und laden

Test-Suite sichern

suite = testsuite(pwd);
save suite.mat suite

Test-Suite laden

load suite.mat
results = run(suite);

Test Browser

Abb. 1: MATLAB Test Browser

Der MATLAB Test Browser ist eine grafische Oberfläche des MATLAB Unit Test Frameworks, mit der sich automatisierte Tests komfortabel verwalten und ausführen lassen. Er bietet einen schnellen Überblick über vorhandene Testdateien, Testklassen und Testmethoden und ermöglicht die gezielte Auswahl einzelner oder mehrerer Tests. Die Ergebnisse werden übersichtlich dargestellt, sodass erfolgreiche, fehlgeschlagene oder übersprungene Tests leicht erkannt und analysiert werden können. Dadurch unterstützt der Test Browser Entwickler dabei, die Qualität und Zuverlässigkeit von MATLAB-Code sicherzustellen und Fehler frühzeitig zu erkennen, ohne Tests ausschließlich über die Kommandozeile ausführen zu müssen.

Sie finden ihn im Panel ab R2025a in dem auch der Copilot Chat zu finden ist.

Praktische Übung

Aufgabe 11.1

Implementieren Sie:

function y = cube(x)

y=x3

Erstellen Sie mindestens drei Unit-Tests.

Aufgabe 11.2

Entwickeln Sie Tests für:

function y = celsius2fahrenheit(x)

Prüfen Sie:

  • 0 °C
  • 100 °C
  • −40 °C

Aufgabe 11.3 – Butterworth-Tiefpassfilter

Ein Butterworth-Tiefpassfilter 2. Ordnung soll getestet werden.

Entwickeln Sie einen Test, der nachweist:

  • Ein konstantes Signal bleibt konstant.
  • Die Ausgangslänge entspricht der Eingangslänge.

Aufgabe 11.4 – Testklasse entwickeln

Gegeben:

function y = addiere(a,b)
    y = a+b;
end

Erstellen Sie eine vollständige Testklasse mit mindestens vier Testfällen.

Aufgabe 11.5 – Fehlerbehandlung testen

Für die Funktion

function y = wurzel(x)

soll ein Fehler bei negativen Werten ausgelöst werden.

  1. Implementieren Sie die Funktion.
  2. Schreiben Sie den entsprechenden Unit-Test.

Aufgabe 11.6 – Signalverarbeitung

Eine Filterfunktion soll getestet werden.

Prüfen Sie:

  • Ausgangslänge
  • Konstantes Eingangssignal
  • Keine NaN-Werte im Ergebnis

Implementieren Sie die Tests mit verifyEqual, verifyTrue und verifyFalse.

Fazit

Der Modultest in MATLAB dient dazu, die korrekte Funktion von Programmen systematisch und automatisiert zu überprüfen. Im Zentrum steht das Unit-Test-Framework von MATLAB.

Für wissenschaftliche Algorithmen, Signalverarbeitung und Simulink-Modelle ist MATLAB mit seinem Unit-Test-Framework besonders geeignet.

Vorteile

  • frühzeitige Fehlererkennung
  • reproduzierbare Ergebnisse
  • sichere Weiterentwicklung
  • Dokumentation des Soll-Verhaltens
  • Grundlage für kontinuierliche Integration



MATLAB® Befehlsübersicht
→ zurück zum Hauptartikel: BSE Modellierung und Simulation - SoSe26