Dieser Artikel ist Ihre Aufmerksamkeit wert, wenn Sie Schreiben Sie leidenschaftlich gerne qualitativ hochwertige Software und möchten Sie die Stabilität Ihrer App durch Tests verbessern. Sind Sie es leid, dass in Ihren Produktionssystemen unerwartete Fehler auftauchen? Benötigen Sie Hilfe zu verstehen, was automatisierte Tests sind und wie man sie angeht. Warum brauchen wir automatisierte Tests? Als Ingenieure wollen wir , aber mit jeder neuen Funktion, die wir erstellen, erhöhen wir unweigerlich die Größe und Komplexität unserer Apps. Dinge bauen , die funktionieren Wenn das Produkt wächst, wird es immer zeitaufwendiger, alle von Ihren Änderungen betroffenen Funktionen (z. B. mit Ihren Händen) zu testen. manuell Das Fehlen automatisierter Tests führt dazu, dass wir entweder zu viel Zeit aufwenden und unsere Versandgeschwindigkeit verlangsamen oder zu wenig ausgeben, um die Geschwindigkeit zu sparen, was zusammen mit den nächtlichen Anrufen von PagerDuty zu neuen Fehlern im Rückstand führt. Im Gegenteil, . Also Computer können so programmiert werden, dass sie immer wieder dasselbe tun delegieren wir das Testen an Computer! Arten von Tests Die schlägt . Lassen Sie uns tief in jede Art eintauchen und verstehen, warum wir jede brauchen. Idee der Testpyramide drei Haupttypen von Tests vor: Unit-, Integrations- und End-to-End-Tests Unit-Tests Eine ist ein kleines Stück Logik, das Sie testen (ohne sich auf andere Komponenten zu verlassen). Einheit isoliert Sie sind innerhalb von Sekunden fertig. können sie sie jederzeit lokal und auf CI ausführen, ohne die abhängigen Dienste hochzufahren bzw. API- und Datenbankaufrufe durchzuführen. Unit-Tests sind schnell. Durch die Isolation Eine Funktion, die zwei Zahlen akzeptiert und diese summiert. Wir wollen es mit unterschiedlichen Argumenten aufrufen und sicherstellen, dass der zurückgegebene Wert korrekt ist. Beispiel für einen Unit-Test: // Function "sum" is the unit const sum = (x, y) => x + y test('sums numbers', () => { // Call the function, record the result const result = sum(1, 2); // Assert the result expect(result).toBe(3) }) test('sums numbers', () => { // Call the function, record the result const result = sum(5, 10); // Assert the result expect(result).toBe(15) }) Ein interessanteres Beispiel ist die React-Komponente, die Text rendert, nachdem die API-Anfrage abgeschlossen ist. Wir müssen das API-Modul nachahmen, um die notwendigen Werte für unsere Tests zurückzugeben, die Komponente zu rendern und sicherzustellen, dass der gerenderte HTML-Code den von uns benötigten Inhalt enthält. // "MyComponent" is the unit const MyComponent = () => { const { isLoading } = apiModule.useSomeApiCall(); return isLoading ? <div>Loading...</div> : <div>Hello world</div> } test('renders loading spinner when loading', () => { // Mocking the API module, so that it returns the value we need jest.mock(apiModule).mockReturnValue(() => ({ useSomeApiCall: jest.fn(() => ({ // Return "isLoading: false" for this test case isLoading: false })) })) // Execute the unit (render the component) const result = render(<MyComponent />) // Assert the result result.findByText('Loading...').toBeInTheDocument() }) test('renders text content when not loading', () => { // Mocking the API module jest.mock(apiModule).mockReturnValue(() => ({ useSomeApiCall: jest.fn(() => ({ // Return "isLoading: false" for this test case isLoading: false })) })) // Execute the unit (render the component) const result = render(<MyComponent />) // Assert the result result.findByText('Hello world').toBeInTheDocument() }) Integrationstests Wenn Ihre mit anderen , nennen wir das eine . Diese Tests sind langsamer als Unit-Tests, testen aber, wie die Teile Ihrer App miteinander verbunden sind. Einheit Einheiten interagiert (Abhängigkeiten) Integration Ein Dienst, der Benutzer in einer Datenbank erstellt. Dies erfordert, dass eine DB-Instanz ( ) verfügbar ist, wenn die Tests ausgeführt werden. Wir werden testen, ob der Dienst einen Benutzer aus der Datenbank erstellen und abrufen kann. Beispiel für einen Integrationstest: Abhängigkeit import db from 'db' // We will be testing "createUser" and "getUser" const createUser = name => db.createUser(name) // creates a user const getUser = name => db.getUserOrNull(name) // retrieves a user or null test("creates and retrieves users", () => { // Try to get a user that doesn't exist, assert Null is returned const nonExistingUser = getUser("i don't exist") expect(nonExistingUser).toBe(null); // Create a user const userName = "test-user" createUser(userName); // Get the user that was just created, assert it's not Null const user = getUser(userName); expect(user).to.not.be(null) }) End-to-End-Tests Es handelt sich um einen Test, bei dem wir die testen, bei der alle ihre Abhängigkeiten verfügbar sind. Diese Tests simulieren am besten das tatsächliche Benutzerverhalten und ermöglichen es Ihnen, in Ihrer App zu erkennen, sie sind jedoch die Art von Tests. End-to-End- vollständig bereitgestellte App alle möglichen Probleme langsamste Wann immer Sie End-to-End-Tests durchführen möchten, müssen Sie die gesamte Infrastruktur bereitstellen und sicherstellen, dass in Ihrer Umgebung Drittanbieter verfügbar sind. Sie möchten sie für die Funktionen Ihrer App haben. nur geschäftskritischen Anmeldefluss. Wir möchten zur App gehen, die Anmeldedaten eingeben, sie absenden und die Willkommensnachricht sehen. Schauen wir uns ein End-to-End-Testbeispiel an: test('user can log in', () => { // Visit the login page page.goto('https://example.com/login'); // Fill in the login form page.fill('#username', 'john'); page.fill('#password', 'some-password'); // Click the login button page.click('#login-button'); // Assert the welcome message is visible page.assertTextVisible('Welcome, John!') }) Wie wählen Sie aus, welche Art von Test Sie schreiben möchten? Denken Sie daran, dass und Tests. End-to-End-Tests langsamer sind als Integrationstests Integrationstests langsamer als Unit- Wenn die Funktion, an der Sie arbeiten, geschäftskritisch ist, sollten Sie erwägen, mindestens einen Test zu schreiben (z. B. zu überprüfen, wie die Anmeldefunktion bei der Entwicklung des Authentifizierungsablaufs funktioniert). End-to-End- Neben geschäftskritischen Abläufen wollen wir so viele Randfälle und verschiedene Zustände der Funktion wie möglich testen. können wir testen, wie die Teile der App zusammenarbeiten. Mit Integrationstests Endpunkte sollten die Vorgänge ausführen, das erwartete Ergebnis liefern und keine unerwarteten Fehler auslösen. Es ist eine gute Idee, Integrationstests für Endpunkte und Clientkomponenten durchzuführen. Clientkomponenten sollten den richtigen Inhalt anzeigen und auf Benutzerinteraktionen so reagieren, wie Sie es erwarten. Und schließlich: Wann sollten wir wählen? Alle kleinen Funktionen, die isoliert getestet werden können, wie z. B. , das die Zahlen summiert, , der -Tag rendert, sind gute Kandidaten für Unit-Tests. Einheiten sind perfekt, wenn Sie den Ansatz verfolgen. Unit-Tests sum Button <button> Test Driven Development- Was kommt als nächstes? (Aber klein anfangen) Schreiben Sie einige Tests! , das zu Ihrem Projekt/Ihrer Sprache passt. Jede Sprache verfügt über eine beliebte Bibliothek zum Testen, z. B. / für JavaScript, / für End-to-End (verwendet auch JavaScript), für Java usw. Installieren Sie ein Test-Framework Jest Vitest Cypress Playwright JUnit Finden Sie eine kleine Funktion in Ihrem Projekt und schreiben Sie einen Test dafür. Unit- Schreiben Sie einen für eine Interaktion zwischen Komponente, Dienst und Datenbank. Integrationstest Wählen Sie ein kritisches Szenario, das schnell getestet werden kann, beispielsweise einen einfachen Anmeldeablauf, und schreiben Sie dafür einen Test. End-to-End- Führen Sie die oben genannten Schritte einmal aus, um zu verstehen, wie es funktioniert. Wiederholen Sie den Vorgang dann während einiger Funktions-/Fehlerarbeiten. Teilen Sie es dann mit Ihren Kollegen, damit Sie alle Tests schreiben, Zeit sparen und nachts besser schlafen können! Nützliche Ressourcen: Mein persönlicher Blog von Die praktische Testpyramide Ham Vocke von Testgetriebene Entwicklung Martin Fowler