Suche…


Bemerkungen

Unit-Tests beschreibt den Prozess, bei dem einzelne Codeeinheiten isoliert von dem System geprüft werden, zu dem sie gehören. Was eine Einheit ausmacht, kann von System zu System variieren und von einer einzelnen Methode bis zu einer Gruppe eng verwandter Klassen oder eines Moduls reichen.

Die Einheit wird von ihren Abhängigkeiten mit Hilfe von Testverdopplungen bei Bedarf isoliert und in einen bekannten Zustand versetzt. Ihr Verhalten als Reaktion auf Reize (Methodenaufrufe, Ereignisse, simulierte Daten) wird dann anhand des erwarteten Verhaltens getestet.

Unit-Tests für ganze Systeme können mit benutzerdefinierten schriftlichen Test-Kabeln durchgeführt werden. Es wurden jedoch viele Test-Frameworks geschrieben, um den Prozess zu rationalisieren und einen Großteil der Aufgaben im Bereich Sanitär-, Wiederholungs- und Alltagsaufgaben zu erledigen. Dadurch können sich die Entwickler auf das konzentrieren, was sie testen möchten.

Wenn für ein Projekt genügend Unit-Tests vorhanden sind, können Änderungen am Hinzufügen neuer Funktionen oder an einem Code-Refactoring problemlos durchgeführt werden, indem am Ende überprüft wird, dass alles wie zuvor funktioniert.

Code Coverage , normalerweise in Prozent ausgedrückt, ist die typische Metrik, die verwendet wird, um zu zeigen, wie viel Code in einem System von Unit Tests abgedeckt wird. Beachten Sie, dass es keine strenge Regel gibt, wie hoch dies sein sollte. Es wird jedoch allgemein akzeptiert, dass je höher, desto besser.

Test Driven Development (TDD) ist ein Prinzip, das vorsieht, dass ein Entwickler mit dem Codieren beginnen soll, indem er einen fehlerhaften Unit-Test schreibt und erst dann den Produktionscode schreibt, der den Test bestanden hat. Beim Üben von TDD kann gesagt werden, dass die Tests selbst der erste Benutzer des erstellten Codes sind. Sie helfen daher, das Design des Codes zu überprüfen und voranzutreiben, damit er so einfach zu verwenden und so robust wie möglich ist.

Versionen

Unit Testing ist ein Konzept, das keine Versionsnummern hat.

Ein grundlegender Unit-Test

Im einfachsten Fall besteht ein Komponententest aus drei Stufen:

  • Bereiten Sie die Umgebung für den Test vor
  • Führen Sie den zu testenden Code aus
  • Überprüfen Sie, ob das erwartete Verhalten mit dem beobachteten Verhalten übereinstimmt

Diese drei Phasen werden häufig als Arrange-Act-Assert oder Gegeben-Wann-Dann-Zustand bezeichnet.

Unten ist ein Beispiel in C #, das das NUnit- Framework verwendet.

[TestFixture]
public CalculatorTest
{
   [Test]
   public void Add_PassSevenAndThree_ExpectTen()
   {
       // Arrange - setup environment
       var systemUnderTest = new Calculator();         

       // Act - Call system under test
       var calculatedSum = systemUnderTest.Add(7, 3);  
       
       // Assert - Validate expected result
       Assert.AreEqual(10, calculatedSum);             
  }
}

Bei Bedarf wird eine optionale vierte Reinigungsstufe aufgeräumt.

Ein Unit-Test mit abgestumpfter Abhängigkeit

Gute Unit-Tests sind unabhängig, aber Code hat oft Abhängigkeiten. Wir verwenden verschiedene Arten von Testverdopplungen , um die Abhängigkeiten für das Testen zu entfernen. Einer der einfachsten Test-Doubles ist ein Stub. Dies ist eine Funktion mit einem hart codierten Rückgabewert, der anstelle der realen Abhängigkeit aufgerufen wird.

// Test that oneDayFromNow returns a value 24*60*60 seconds later than current time

let systemUnderTest = new FortuneTeller()       // Arrange - setup environment
systemUnderTest.setNow(() => {return 10000})    //   inject a stub which will 
                                                //   return 10000 as the result

let actual = systemUnderTest.oneDayFromNow()    // Act - Call system under test

assert.equals(actual, 10000 + 24 * 60 * 60)     // Assert - Validate expected result

In Produktionscode oneDayFromNow nennen würde Date.now (), aber das wäre für widersprüchlich und unzuverlässig Tests machen. Also hier stummeln wir es raus.

Ein Unit-Test mit einem Spion (Interaktionstest)

Bei klassischen Einheitentests wird der Status der Tests geprüft. Es kann jedoch unmöglich sein, Methoden richtig zu testen, deren Verhalten von anderen Klassen durch den Status abhängt. Wir testen diese Methoden durch Interaktionstests , mit denen sichergestellt wird, dass das getestete System seine Mitarbeiter korrekt aufruft. Da die Mitarbeiter ihre eigenen Komponententests haben, ist dies ausreichend und eigentlich ein besserer Test der tatsächlichen Verantwortung der getesteten Methode. Wir testen nicht, ob diese Methode bei einer Eingabe ein bestimmtes Ergebnis zurückgibt, sondern ruft die entsprechenden Mitarbeiter korrekt auf.

// Test that squareOfDouble invokes square() with the doubled value

let systemUnderTest = new Calculator()          // Arrange - setup environment
let square = spy()
systemUnderTest.setSquare(square)               //   inject a spy

let actual = systemUnderTest.squareOfDouble(3)  // Act - Call system under test

assert(square.calledWith(6))                    // Assert - Validate expected interaction

Einfacher Java + JUnit-Test

JUnit ist das führende Testframework zum Testen von Java-Code.

Die getestete Klasse modelliert ein einfaches Bankkonto, das eine Strafe verlangt, wenn Sie überzogen werden.

public class BankAccount {
    private int balance;

    public BankAccount(int i){
        balance = i;
    }

    public BankAccount(){
        balance = 0;
    }

    public int getBalance(){
        return balance;
    }

    public void deposit(int i){
        balance += i;
    }

    public void withdraw(int i){
        balance -= i;
        if (balance < 0){
            balance -= 10; // penalty if overdrawn
        }
    }
}

Diese BankAccount überprüft das Verhalten einiger öffentlicher Methoden von BankAccount .

import org.junit.Test;
import static org.junit.Assert.*;

// Class that tests
public class BankAccountTest{

    BankAccount acc;

    @Before                        // This will run **before** EACH @Test
    public void setUptestDepositUpdatesBalance(){
        acc = new BankAccount(100);  
    } 

    @After                        // This Will run **after** EACH @Test
    public void tearDown(){
    // clean up code
    }

    @Test
    public void testDeposit(){
       // no need to instantiate a new BankAccount(), @Before does it for us

        acc.deposit(100);

        assertEquals(acc.getBalance(),200); 
    }

    @Test
    public void testWithdrawUpdatesBalance(){    
        acc.withdraw(30);

        assertEquals(acc.getBalance(),70); // pass
    }

    @Test
    public void testWithdrawAppliesPenaltyWhenOverdrawn(){

        acc.withdraw(120);

        assertEquals(acc.getBalance(),-30);
    }
}

Komponententest mit Parametern mit NUnit und C #

using NUnit.Framework;

namespace MyModuleTests 
{
    [TestFixture]
    public class MyClassTests
    {
        [TestCase(1, "Hello", true)]
        [TestCase(2, "bye", false)]
        public void MyMethod_WhenCalledWithParameters_ReturnsExpected(int param1, string param2, bool expected)
        {
        //Arrange
        var foo = new MyClass(param1);

        //Act
        var result = foo.MyMethod(param2);

        //Assert
        Assert.AreEqual(expected, result);
        }
    }
}

Ein grundlegender Python-Unit-Test

import unittest

def addition(*args):
    """ add two or more summands and return the sum """

    if len(args) < 2:
        raise ValueError, 'at least two summands are needed'
    
    for ii in args: 
        if not isinstance(ii, (int, long, float, complex )):
            raise TypeError

    # use build in function to do the job
    return sum(args) 

Nun zum Testteil:

class Test_SystemUnderTest(unittest.TestCase):

    def test_addition(self):
        """test addition function"""

        # use only one summand - raise an error 
        with self.assertRaisesRegexp(ValueError, 'at least two summands'):
            addition(1)
        
        # use None - raise an error
        with self.assertRaises(TypeError):
            addition(1, None)
        
        # use ints and floats 
        self.assertEqual(addition(1, 1.), 2)

        # use complex numbers
        self.assertEqual(addition(1, 1., 1+2j), 3+2j)

if __name__ == '__main__':
    unittest.main()

Ein XUnit-Test mit Parametern

using Xunit;

public class SimpleCalculatorTests
{
    [Theory]
    [InlineData(0, 0, 0, true)]
    [InlineData(1, 1, 2, true)]
    [InlineData(1, 1, 3, false)]
    public void Add_PassMultipleParameters_VerifyExpected(
        int inputX, int inputY, int expected, bool isExpectedCorrect)
    {
        // Arrange
        var sut = new SimpleCalculator();

        // Act
        var actual = sut.Add(inputX, inputY);

        // Assert
        if (isExpectedCorrect)
        {
            Assert.Equal(expected, actual);
        }
        else
        {
            Assert.NotEqual(expected, actual);
        }
    }
}

public class SimpleCalculator
{
    public int Add(int x, int y)
    {
        return x + y;
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow