unit-testing Samouczek
Rozpoczęcie testów jednostkowych
Szukaj…
Uwagi
Testowanie jednostkowe opisuje proces testowania poszczególnych jednostek kodu w oderwaniu od systemu, którego są częścią. To, co stanowi jednostkę, może się różnić w zależności od systemu, od indywidualnej metody do grupy ściśle powiązanych klas lub modułu.
Jednostka jest izolowana od swoich zależności za pomocą podwójnych testów, gdy jest to konieczne i ustawiana w znany stan. Jego zachowanie w reakcji na bodźce (wywołania metod, zdarzenia, dane symulowane) jest następnie testowane pod kątem oczekiwanego zachowania.
Testy jednostkowe całych systemów mogą być wykonywane przy użyciu niestandardowych pisemnych wiązek testowych, jednak napisano wiele ram testowych, aby usprawnić proces i zająć się wieloma czynnościami hydraulicznymi, powtarzalnymi i przyziemnymi. Pozwala to programistom skoncentrować się na tym, co chcą przetestować.
Gdy projekt ma wystarczającą liczbę testów jednostkowych, dowolną modyfikację dodania nowej funkcjonalności lub wykonania refaktoryzacji kodu można łatwo zrobić, weryfikując na końcu, że wszystko działa jak poprzednio.
Pokrycie kodu , zwykle wyrażone w procentach, jest typową miarą używaną do pokazania, ile kodu w systemie jest objęte testami jednostkowymi; zwróć uwagę, że nie ma twardej i szybkiej reguły dotyczącej tego, jak wysoka powinna być ta wysokość, ale ogólnie przyjmuje się, że im wyższa, tym lepsza.
Test Driven Development (TDD) to zasada określająca, że programista powinien rozpocząć kodowanie od napisania nieudanego testu jednostkowego, a dopiero potem napisać kod produkcyjny, który przejdzie test. Ćwicząc TDD, można powiedzieć, że same testy są pierwszymi konsumentami tworzonego kodu; dlatego pomagają kontrolować i sterować projektem kodu, dzięki czemu jest on tak prosty w użyciu i jak najbardziej niezawodny.
Wersje
Testy jednostkowe to koncepcja, która nie ma numerów wersji.
Podstawowy test jednostkowy
W najprostszym przypadku test jednostkowy składa się z trzech etapów:
- Przygotuj środowisko do testu
- Wykonaj kod do przetestowania
- Sprawdź, czy oczekiwane zachowanie pasuje do obserwowanego zachowania
Te trzy etapy są często nazywane „Arrange-Act-Assert” lub „Given-When-Then”.
Poniżej znajduje się przykład w języku C #, który używa frameworka NUnit .
[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);
}
}
W razie potrzeby porządkuje się opcjonalny czwarty etap czyszczenia.
Test jednostkowy z zalążkową zależnością
Dobre testy jednostkowe są niezależne, ale kod często ma zależności. Używamy różnego rodzaju podwójnych testów, aby usunąć zależności do testowania. Jednym z najprostszych podwójnych testów jest odgałęzienie. Jest to funkcja z zakodowaną wartością zwrotną wywoływaną zamiast zależności w świecie rzeczywistym.
// 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
W kodzie produkcyjnym oneDayFromNow
wywołałaby Date.now (), ale spowodowałoby to niespójne i niewiarygodne testy. Więc tutaj to rozwalamy.
Test jednostkowy ze szpiegiem (test interakcji)
Klasyczne testy jednostkowe testują stan , ale prawidłowe testowanie metod, których zachowanie zależy od innych klas, może być niemożliwe. Testujemy te metody za pomocą testów interakcji , które sprawdzają, czy testowany system poprawnie wzywa swoich współpracowników. Ponieważ współpracownicy mają własne testy jednostkowe, jest to wystarczające, a właściwie lepszy test rzeczywistej odpowiedzialności za testowaną metodę. Nie testujemy, czy ta metoda zwraca określony wynik na podstawie danych wejściowych, ale zamiast tego poprawnie wywołuje swoich współpracowników.
// 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
Prosty test Java + JUnit
JUnit to wiodąca platforma testowa używana do testowania kodu Java.
Testowana klasa modeluje proste konto bankowe, które pobiera karę za przekroczenie kwoty.
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
}
}
}
Ta klasa testowa sprawdza zachowanie niektórych metod publicznych 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);
}
}
Test jednostkowy z parametrami za pomocą NUnit i 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);
}
}
}
Podstawowy test jednostki python
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)
Teraz część testowa:
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()
Test XUnit z parametrami
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;
}
}