Erste Schritte mit Apex-Einheitentests
Lernziele
Nachdem Sie diese Lektion abgeschlossen haben, sind Sie in der Lage, die folgenden Aufgaben auszuführen:
- Beschreiben der wichtigsten Vorteile von Apex-Einheitentests
- Definieren einer Klasse mit Testmethoden
- Ausführen aller Testmethoden in einer Klasse und Untersuchen von Fehlschlägen
- Erstellen und Ausführen einer Suite mit Testklassen
Apex-Einheitentests
Mithilfe des Apex-Test-Framework können Sie für Ihre Apex-Klassen und -Auslöser auf der Lightning-Plattform Tests schreiben und ausführen. Apex-Einheitentests stellen eine hohe Qualität Ihres Apex-Codes sicher und ermöglichen Ihnen, die Anforderungen für die Bereitstellung von Apex zu erfüllen.
Tests sind der Schlüssel zu einer erfolgreichen langfristigen Entwicklung und ein wichtiger Bestandteil des Entwicklungsprozesses. Das Apex-Test-Framework bietet eine einfache Möglichkeit zum Testen Ihres Apex-Codes. Apex-Code kann nur in einer Sandbox-Umgebung oder einer Entwicklerorganisation geschrieben werden, jedoch nicht in der Produktion. Apex-Code kann aus einer Sandbox in einer Produktionsorganisation bereitgestellt werden. Außerdem können Anwendungsentwickler Apex-Code aus ihren Entwicklerorganisationen an Kunden verteilen, indem sie Pakete in AppExchange auf der Lightning-Plattform hochladen. Apex-Einheitentests sind nicht nur von entscheidender Bedeutung für die Qualitätssicherung, sondern auch Voraussetzungen für die Bereitstellung und Verteilung von Apex.
Apex-Einheitentests bieten folgende Vorteile:
- Sicherstellen, dass Ihre Apex-Klassen und -Auslöser erwartungsgemäß funktionieren
- Verwenden einer Suite von Regressionstests, die bei jeder Aktualisierung von Klassen und Auslösern erneut ausgeführt werden können, um sicherzustellen, dass zukünftige Änderungen, die Sie an Ihrer Anwendung vornehmen, bestehende Funktionen nicht beschädigen
- Einhalten der Anforderungen für die Codeabdeckung für die Bereitstellung von Apex in der Produktion oder Verteilung von Apex an Kunden über Pakete
- Bereitstellen hochwertiger Anwendungen in der Produktionsorganisation, wodurch die Produktivität von Produktionsbenutzern gesteigert wird
- Bereitstellen hochwertiger Anwendungen für Paketabonnenten, wodurch das Vertrauen Ihrer Kunden erhöht wird
Anforderungen für die Codeabdeckung für die Bereitstellung
Bevor Sie Ihren Code bereitstellen oder in AppExchange auf der Lightning-Plattform als Paket erstellen können, müssen mindestens 75 % des Apex-Codes durch Tests abgedeckt sein und alle diese Tests müssen erfolgreich abgeschlossen worden sein. Zudem müssen alle Auslöser eine gewisse Abdeckung aufweisen. Codeabdeckung ist zwar eine Voraussetzung für die Bereitstellung, Sie sollten jedoch Tests nicht nur dafür schreiben, diese Voraussetzung zu erfüllen, sondern die allgemeinen Anwendungsfälle in Ihrer Anwendung testen, darunter positive und negative Testfälle sowie die Verarbeitung von mehreren oder einzelnen Datensätzen.
Syntax von Testmethoden
Testmethoden werden mit der Anmerkung @isTest
definiert und haben die folgende Syntax:
@isTest static void testName() { // code_block }
Die Anmerkung @isTest
akzeptiert mehrere in Klammern gesetzte und durch Leerzeichen getrennte Modifikatoren. Einer dieser Parameter wird an späterer Stelle behandelt.
Die Sichtbarkeit einer Testmethode ist nicht von Bedeutung, es spielt also keine Rolle, ob eine Testmethode als öffentlich oder privat deklariert wird, da das Test-Framework stets auf Testmethoden zugreifen kann. Aus diesem Grund enthält die Syntax keine Zugriffsmodifikatoren.
Testmethoden müssen in Testklassen, d. h. Klassen, die mit der Anmerkung @isTest
gekennzeichnet sind, definiert werden. Diese Testklasse zeigt eine Definition einer Testklasse mit einer Testmethode.
@isTest private class MyTestClass { @isTest static void myTest() { // code_block } }
Testklassen können entweder privat oder öffentlich sein. Wenn Sie eine Testklasse nur für Einheitentests verwenden, deklarieren Sie sie als privat. Öffentliche Testklassen werden normalerweise für Testdaten-Factory-Klassen verwendet, die später behandelt werden.
Beispiel für einen Einheitentest: Test der TemperatureConverter-Klasse
Das folgende einfache Beispiel zeigt eine Testklasse mit drei Testmethoden. Die getestete Klassenmethode verwendet eine Temperaturangabe in Fahrenheit als Eingabe. Sie rechnet diese Temperatur in Celsius um und gibt das umgerechnete Ergebnis zurück. Fügen Sie die benutzerdefinierte Klasse und ihre Testklasse hinzu.
- Klicken Sie in der Developer Console auf File (Datei) | New (Neu) | Apex Class (Apex-Klasse) und geben Sie als Klassennamen
TemperatureConverter
ein. Klicken Sie dann auf OK.
- Ersetzen Sie den Standardtext der Klasse durch Folgendes:
public class TemperatureConverter { // Takes a Fahrenheit temperature and returns the Celsius equivalent. public static Decimal FahrenheitToCelsius(Decimal fh) { Decimal cs = (fh - 32) * 5/9; return cs.setScale(2); } }
- Drücken Sie Strg+S, um die Klasse zu speichern.
- Wiederholen Sie die vorherigen Schritte, um die Klasse
TemperatureConverterTest
zu erstellen. Fügen Sie dieser Klasse Folgendes hinzu:@isTest private class TemperatureConverterTest { @isTest static void testWarmTemp() { Decimal celsius = TemperatureConverter.FahrenheitToCelsius(70); System.assertEquals(21.11,celsius); } @isTest static void testFreezingPoint() { Decimal celsius = TemperatureConverter.FahrenheitToCelsius(32); System.assertEquals(0,celsius); } @isTest static void testBoilingPoint() { Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212); System.assertEquals(100,celsius,'Boiling point temperature is not expected.'); } @isTest static void testNegativeTemp() { Decimal celsius = TemperatureConverter.FahrenheitToCelsius(-10); System.assertEquals(-23.33,celsius); } }
Die Testklasse TemperatureConverterTest
prüft, dass die Methode erwartungsgemäß funktioniert, indem sie sie mit unterschiedlichen Eingaben für die Temperatur in Fahrenheit aufruft. Jede Testmethode überprüft einen Eingabetyp: eine warme Temperatur, die Gefrierpunkttemperatur, die Siedepunkttemperatur und eine negative Temperatur. Die Überprüfungen werden durchgeführt, indem die Methode System.assertEquals()
aufgerufen wird, die zwei Parameter verwendet: der erste ist der erwartete Wert und der zweite der tatsächliche Wert. Es gibt eine weitere Version dieser Methode, die einen dritten Parameter verwendet – eine Zeichenfolge, die den durchgeführten Vergleich beschreibt. Dieser wird in testBoilingPoint()
verwendet. Diese optionale Zeichenfolge wird protokolliert, wenn die Behauptung nicht zutrifft.
Führen Sie die Methoden in dieser Klasse aus.
- Klicken Sie in der Developer Console auf Test | New Run (Neuer Lauf).
- Klicken Sie unter Test Classes (Testklassen) auf TemperatureConverterTest.
- Um alle Testmethoden in der Klasse
TemperatureConverterTest
zur Testausführung hinzuzufügen, klicken Sie auf Add Selected (Ausgewählte hinzufügen).
- Klicken Sie auf Run (Ausführen).
- Auf der Registerkarte "Tests" wird der Status Ihrer Tests während der Ausführung angezeigt. Erweitern Sie den Testlauf solange, bis die Liste der einzelnen Tests angezeigt wird, die ausgeführt wurden. Sie sind alle mit einem grünen Häkchen markiert.
Nachdem Sie die Tests ausgeführt haben, wird für die Apex-Klassen und -Auslöser in der Organisation automatisch eine Codeabdeckung generiert. Sie können den Prozentsatz der Codeabdeckung auf der Registerkarte "Tests" der Developer Console einsehen. In diesem Beispiel hat die getestete Klasse, die Klasse TemperatureConverter
, eine Abdeckung von 100 %, wie in folgender Abbildung dargestellt.
Zwar würde eine Testmethode bereits eine vollständige Abdeckung der TemperatureConverter
-Klasse ergeben, dennoch ist es wichtig, verschiedene Eingaben zu testen, um die Qualität des Codes sicherzustellen. Es ist natürlich nicht möglich, jeden Datenpunkt zu überprüfen, doch Sie können allgemeine Datenpunkte und unterschiedliche Eingabebereiche testen. Beispielsweise können Sie die Übergabe von positiven und negativen Zahlen, Grenzwerten und ungültigen Parameterwerten testen, um negatives Verhalten zu überprüfen. Die Tests für die TemperatureConverter
-Klasse überprüfen allgemeine Datenpunkte, wie Siedetemperatur und negative Temperaturen.
Die TemperatureConverterTest
-Testklasse deckt keine ungültigen Eingaben oder Grenzbedingungen ab. Grenzbedingungen beziehen sich auf Mindest- und Höchstwerte. In diesem Fall akzeptiert die Methode zur Temperaturumrechnung Werte des Typs Decimal
, der hohe Zahlen akzeptieren kann, höher als Werte des Typs Double
. Was ungültige Eingaben betrifft, gibt es keine ungültige Temperatur, sondern die einzige ungültige Eingabe ist NULL. Wie behandelt die Umrechnungsmethode diesen Wert? Wenn die Apex-Laufzeit die Parametervariable dereferenziert, um die Formel auszuwerten, wird in diesem Fall eine System.NullPointerException
ausgelöst. Sie können die Methode FahrenheitToCelsius()
ändern, um auf eine ungültige Eingabe zu prüfen und in diesem Fall NULL zurückzugeben, und dann einen Test zum Überprüfen des Verhaltens bei ungültiger Eingabe hinzufügen.
Bis zu diesem Punkt werden alle Tests erfolgreich abgeschlossen, da die in der Klassenmethode verwendete Umrechnungsformel korrekt ist. Das ist jedoch langweilig! Versuchen wir, einen Fehlschlag zu simulieren, um zu sehen, was passiert, wenn eine Behauptung nicht zutrifft. Ändern Sie beispielsweise den Test für die Siedepunkttemperatur und übergeben Sie einen falschen erwarteten Wert für die Siedepunkttemperatur in Celsius (0 anstelle von 100). Dies führt dazu, dass die entsprechende Testmethode fehlschlägt.
- Ändern Sie die Testmethode
testBoilingPoint()
wie folgt:@isTest static void testBoilingPoint() { Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212); // Simulate failure System.assertEquals(0,celsius,'Boiling point temperature is not expected.'); }
- Um denselben Testlauf auszuführen, klicken Sie auf der Registerkarte "Tests" auf den letzten Testlauf und dann auf Test | Rerun (Erneut ausführen). Die Behauptung in
testBoilingPoint()
trifft nicht zu und löst einen schwerwiegenden Fehler aus (eineAssertException
, die nicht abgefangen werden kann).
- Überprüfen Sie die Ergebnisse auf der Registerkarte "Tests", indem Sie den letzten Testlauf erweitern. Für den Testlauf wird angegeben, dass einer von vier Tests fehlgeschlagen ist. Um mehr Details über den Fehlschlag abzurufen, doppelklicken Sie auf den Testlauf. Die detaillierten Ergebnisse werden in einer separaten Registerkarte angezeigt, wie in dieser Abbildung dargestellt.
[Alt text: Überprüfen der Ergebnisse eines fehlgeschlagenen Tests in der Developer Console]
- Um die Fehlermeldungen für den fehlgeschlagenen Test abzurufen, doppelklicken Sie in die Spalte "Errors (Fehler)" des fehlgeschlagenen Tests. Der angezeigte Beschreibungstext neben
Assertion Failed:
ist der Text, den wir in der AnweisungSystem.assertEquals()
angegeben haben.System.AssertException:Assertion Failed:Boiling point temperature is not expected.:Expected:0, Actual:100.00
Die Testdaten in diesen Testmethoden sind Zahlen und keine Salesforce-Datensätze. Im nächsten Abschnitt erfahren Sie mehr darüber, wie Salesforce-Datensätze getestet werden und wie Sie Ihre Daten einrichten.
Erhöhen der Codeabdeckung
Versuchen Sie beim Schreiben von Tests, die höchstmögliche Codeabdeckung zu erreichen. Streben Sie nicht nur eine Abdeckung von 75 % an, also die niedrigste Abdeckung, die die Lightning-Plattform für Bereitstellungen und Pakete erfordert. Je mehr Testfälle Ihre Tests abdecken, desto höher ist die Wahrscheinlichkeit, dass Ihr Code stabil ist. Selbst wenn Sie Testmethoden für alle Ihre Klassenmethoden geschrieben haben, liegt die Codeabdeckung in manchen Fällen dennoch nicht bei 100 %. Ein häufiger Grund dafür ist, dass nicht alle Datenwerte für eine bedingte Codeausführung abgedeckt werden. Einige Datenwerte werden eventuell ignoriert, wenn Ihre Klassenmethode IF-Anweisungen aufweist, die dazu führen, dass unterschiedliche Zweige ausgeführt werden, je nachdem, ob die ausgewertete Bedingung erfüllt ist oder nicht. Achten Sie darauf, dass Ihre Testmethoden diese unterschiedlichen Werte berücksichtigen.
Dieses Beispiel verdeutlicht die Klassenmethode getTaskPriority()
, die zwei if
-Anweisungen enthält. Die Hauptaufgabe dieser Methode ist es, einen Zeichenfolgenwert für die Priorität basierend auf dem angegebenen Bundesstaat des Leads zurückzugeben. Die Methode prüft den Bundesstaat zuerst und gibt NULL zurück, wenn der Bundesstaat ungültig ist. Wenn der Bundesstaat "CA" lautet, gibt die Methode "High" zurück, und für alle anderen Bundesstaatenwerte gibt sie "Normal" zurück.
public class TaskUtil { public static String getTaskPriority(String leadState) { // Validate input if(String.isBlank(leadState) || leadState.length() > 2) { return null; } String taskPriority; if(leadState == 'CA') { taskPriority = 'High'; } else { taskPriority = 'Normal'; } return taskPriority; } }
Dies ist die Testklasse für die Methode getTaskPriority()
. Die Testmethode ruft getTaskPriority()
mit einem US-Bundesstaat ('NY') auf.
@isTest private class TaskUtilTest { @isTest static void testTaskPriority() { String pri = TaskUtil.getTaskPriority('NY'); System.assertEquals('Normal', pri); } }
Führen Sie diese Testklasse (TaskUtilTest
) in der Developer Console aus und prüfen Sie die Codeabdeckung für die entsprechende TaskUtil
-Klasse, die dieser Test abdeckt. Nach Abschluss des Testlaufs wird die Codeabdeckung für TaskUtil
mit 75 % angezeigt. Wenn Sie diese Klasse in der Developer Console öffnen, sehen Sie sechs blaue Zeilen (die abdeckten Zeilen) und zwei rote Zeilen (die nicht abdeckten Zeilen), wie in der Abbildung dargestellt.
Der Grund, warum Zeile 5 nicht abgedeckt wurde, ist, dass unsere Testklasse keinen Test für die Übergabe eines ungültigen Bundesstaatparameters enthielt. Entsprechend wurde Zeile 11 nicht abgedeckt, da die Testmethode nicht 'CA' als Bundesstaat übergab. Fügen Sie zwei weitere Testmethoden hinzu, um diese Szenarien abzudecken. Unten wird die vollständige Testklasse nach Hinzufügen der Testmethoden testTaskHighPriority()
und testTaskPriorityInvalid()
dargestellt. Wenn Sie diese Testklasse mit Run All (Alle ausführen) oder New Run (Neuer Durchlauf) erneut ausführen, liegt die Codeabdeckung für TaskUtil
jetzt bei 100 %.
@isTest private class TaskUtilTest { @isTest static void testTaskPriority() { String pri = TaskUtil.getTaskPriority('NY'); System.assertEquals('Normal', pri); } @isTest static void testTaskHighPriority() { String pri = TaskUtil.getTaskPriority('CA'); System.assertEquals('High', pri); } @isTest static void testTaskPriorityInvalid() { String pri = TaskUtil.getTaskPriority('Montana'); System.assertEquals(null, pri); } }
Erstellen und Ausführen einer Testsuite
Eine Testsuite ist eine Sammlung von Apex-Testklassen, die zusammen ausgeführt werden. Erstellen Sie beispielsweise eine Suite mit Tests, die Sie immer dann ausführen, wenn Sie eine Bereitstellung vorbereiten oder Salesforce eine neue Version veröffentlicht. Richten Sie in der Developer Console eine Testsuite ein, um eine Gruppe von Testklassen zu definieren, die Sie regelmäßig zusammen ausführen.
Momentan beinhaltet Ihre Organisation zwei Testklassen. Diese zwei Klassen haben nichts miteinander zu tun, doch diese Tatsache ignorieren wir für den Augenblick einfach. Nehmen wir einmal an, es gibt Fälle, in denen Sie diese beiden Testklassen ausführen möchten, ohne sämtliche Tests in der Organisation auszuführen. In diesem Fall erstellen Sie eine Testsuite, die diese beiden Klassen enthält, und führen die Tests dann in der Suite aus.
- Wählen Sie in der Developer Console Test | New Suite (Neue Suite) aus.
- Geben Sie
TempConverterTaskUtilSuite
als Namen für die Suite ein und klicken Sie auf OK.
- Wählen Sie TaskUtilTest aus, halten Sie die STRG-Taste gedrückt und wählen Sie TemperatureConverterTest aus.
- Um die ausgewählten Testklassen zur Sammlung hinzuzufügen, klicken Sie auf >.
- Klicken Sie auf Speichern.
- Wählen Sie Test | New Suite Run (Neue Suite ausführen) aus.
- Wählen Sie TempConverterTaskUtilSuite aus und klicken Sie dann auf >, um
TempConverterTaskUtilSuite
in die Spalte "Selected Test Suites (Ausgewählte Testsuiten)" zu verschieben.
- Klicken Sie auf Run Suites (Suiten ausführen).
- Überwachen Sie den Status Ihrer Tests während der Ausführung auf der Registerkarte "Tests". Erweitern Sie den Testlauf solange, bis die Liste der einzelnen Tests angezeigt wird, die ausgeführt wurden. Genau wie bei der Ausführung einzelner Testmethoden können Sie auf Methodennamen doppelklicken, um detaillierte Testergebnisse anzuzeigen.
Erstellen von Testdaten
Salesforce-Datensätze, die in Testmethoden erstellt werden, werden nicht an die Datenbank übergeben. Sie werden per Rollback zurückgesetzt, sobald die Ausführung des Tests beendet ist. Dieses Rollback-Verhalten ist praktisch für Tests, da Sie Ihre Testdaten nach der Testausführung nicht bereinigen müssen.
Standardmäßig haben Apex-Tests keinen Zugriff auf bereits vorhandene Daten in der Organisation, mit Ausnahme von Setup- und Metadatenobjekten wie die Objekte "Benutzer" oder "Profil". Richten Sie Testdaten für Ihre Tests ein. Durch die Erstellung von Testdaten werden Ihre Tests stabiler und Fehlschläge aufgrund fehlender oder geänderter Daten in der Organisation werden vermieden. Sie können Testdaten direkt in Ihrer Testmethode oder mithilfe einer Test-Dienstprogrammklasse erstellen. Dies wird an späterer Stelle näher beschrieben.
Weitere Infos
- Sie können Salesforce Apex Extension for Visual Studio Code verwenden, um Apex-Tests auszuführen und die Funktionalität Ihres Codes zu überprüfen.
- Sie können bis zu 6 MB an Apex-Code in jeder Organisation speichern. Mit der Anmerkung
@isTest
gekennzeichnete Testklassen werden auf diese Obergrenze nicht angerechnet.
- Testdaten werden zwar per Rollback zurückgesetzt, für Tests wird jedoch keine separate Datenbank verwendet. Aus diesem Grund führt das Einfügen doppelter sObject-Datensätze bei manchen sObjects, die Felder mit eindeutigen Beschränkungen aufweisen, zu einem Fehler.
- Testmethoden senden keine E-Mails.
- Testmethoden können keine Callouts an externe Services senden. In Tests können simulierte Callouts verwendet werden.
- In einem Test durchgeführte SOSL-Suchvorgänge geben leere Ergebnisse zurück. Um vorhersehbare Ergebnisse zu erhalten, verwenden Sie
Test.setFixedSearchResults()
, um die Datensätze zu definieren, die von der Suche zurückgegeben werden sollen.
Ressourcen
-
Apex Developer Guide: Testing Best Practices
-
Apex Developer Guide: What are Apex Unit Tests?
-
Apex Developer Guide: Isolation of Test Data from Organization Data in Unit Tests
-
Salesforce-Hilfe: Überprüfen der Testabdeckung
Vorbereiten auf die praktische Aufgabe
Damit Sie die praktische Aufgabe am Ende dieser Lektion absolvieren können, müssen Sie eine neue Apex-Klasse namens VerifyDate
erstellen. Kopieren Sie dazu den nachfolgend angegebenen Code:
public class VerifyDate { //method to handle potential checks against two dates public static Date CheckDates(Date date1, Date date2) { //if date2 is within the next 30 days of date1, use date2. Otherwise use the end of the month if(DateWithin30Days(date1,date2)) { return date2; } else { return SetEndOfMonthDate(date1); } } //method to check if date2 is within the next 30 days of date1 private static Boolean DateWithin30Days(Date date1, Date date2) { //check for date2 being in the past if( date2 < date1) { return false; } //check that date2 is within (>=) 30 days of date1 Date date30Days = date1.addDays(30); //create a date 30 days away from date1 if( date2 >= date30Days ) { return false; } else { return true; } } //method to return the end of the month of a given date private static Date SetEndOfMonthDate(Date date1) { Integer totalDays = Date.daysInMonth(date1.year(), date1.month()); Date lastDay = Date.newInstance(date1.year(), date1.month(), totalDays); return lastDay; } }