Test jednostkowy (ang. unit test) to technika testowania tworzonego oprogramowania poprzez wykonywanie testów weryfikujących poprawność działania pojedynczych elementów (jednostek) programu - np. metod lub obiektów w programowaniu obiektowym lub procedur w programowaniu proceduralnym. Testowany fragment programu poddawany jest testowi, który wykonuje go i porównuje wynik (np. zwrócone wartości, stan obiektu, wyrzucone wyjątki) z oczekiwanymi wynikami - tak pozytywnymi, jak i negatywnymi (niepowodzenie działania kodu w określonych sytuacjach również może podlegać testowaniu)
Źródło: pl.wikipedia.org
Klasa testowana
public class Kalkulator { public int Suma(int [] x) { if (x == null) throw new ArgumentNullException(); int s = 0; for(int i=1; i < x.Length ; i++) s+=x[i]; return s; } }
public class KalkulatorTest { public void SumaTest() { int[] x = { 1, 2, 3, 4 }; Kalkulator c = new Kalkulator(); int oczekiwanyWynik = 10; int aktualnyWynik = c.Suma(x); if (aktualnyWynik != oczekiwanyWynik) throw new Exception(String.Format("Test oblany: spodziewana wartosc {0}, aktualna wartosc {1}", oczekiwanyWynik, aktualnyWynik)); } public void SumaTestException() { Kalkulator c = new Kalkulator(); try { c.Suma(null); } catch (ArgumentNullException) { return; } throw new Exception("Test oblany"); } }
class Program { static void Main(string[] args) { KalkulatorTest test = new KalkulatorTest(); test.SumaTest(); test.SumaTestException(); Console.WriteLine("Wszystkie testy zaliczone."); } }
[TestClass] public class UnitTestKalkulator { [TestMethod] public void TestSuma10() { // arrange int[] x = { 1, 2, 3, 4 }; Kalkulator c = new Kalkulator(); int oczekiwanyWynik = 10; //act int aktualnyWynik = c.Suma(x); //assert Assert.AreEqual(oczekiwanyWynik, aktualnyWynik); } }
Technika zwinna (agile), zaliczana także do programowania ekstremalnego.
Źródło: Test-driven development Wstęp do Test Driven Development (MSDN)
Wzorzec AAA: Arrange → Act → Assert
Narzędzia i biblioteki wspierające tworzenie testów, ich organizację, automatyzację wykonywania, raportowanie
using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class UnitTestKalkulator { private Kalkulator _calc; [TestInitialize] public void Init() { _calc = new Kalkulator(); } [TestMethod] public void TestSuma10() { int[] x = { 1, 2, 3, 4 }; int oczekiwanyWynik = 10; int aktualnyWynik = _calc.Suma(x); Assert.AreEqual(oczekiwanyWynik, aktualnyWynik); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void TestSumaException() { _calc.Suma(null); } }
using NUnit.Framework; [TestFixture] public class UnitTestKalkulator { private Kalkulator _calc; [SetUp] public void Create() { _calc = new Kalkulator(); } [Test] public void TestSuma10() { int[] x = { 1, 2, 3, 4 }; int oczekiwanyWynik = 10; int aktualnyWynik = _calc.Suma(x); Assert.AreEqual(oczekiwanyWynik, aktualnyWynik); } [Test] [ExpectedException(typeof(ArgumentNullException))] public void TestSumaException() { _calc.Suma(null); } }
using Xunit; public class KalkulatorTest { private Kalkulator _calc; public KalkulatorTest() { _calc = new Kalkulator(); } [Fact] public void TestSuma() { int[] x = { 1, 2, 3, 4 }; int oczekiwanyWynik = 10; int aktualnyWynik = _calc.Suma(x); Assert.Equal(oczekiwanyWynik, aktualnyWynik); } [Fact] public void TestSumaException() { Assert.Throws<ArgumentNullException>(() => _calc.Suma(null)); } }
{2, 4, 70) dla liczb dodatnich całkowitych {-1, 0, 2, 4, 70) dla liczb dodatnich całkowitych TextContext (tworzona automatycznie lub należy dodać ręcznie)private TestContext testContextInstance; public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } }
[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0; Data Source=C:\Data\MathsData.sdf;", "Numbers")] [TestMethod] public void AddIntegers_FromDataSourceTest() { var target = new Maths(); // Access the data int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]); int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]); int expected = Convert.ToInt32(TestContext.DataRow["Sum"]); int actual = target.IntegerMethod(x, y); Assert.AreEqual(expected, actual); }
„xUnit.net is a free, open source, community-focused unit testing tool for the .NET Framework. Written by the original inventor of NUnit v2, xUnit.net is the latest technology for unit testing C#, F#, VB.NET and other .NET languages. xUnit.net works with ReSharper, CodeRush, TestDriven.NET and Xamarin.”
xUnit.net framework (NuGet)xunit.runner.console uruchamianie testów w konsoli xunit.runner.visualstudio integracja z Test Explorer w VS[Theory] [InlineData(3)] [InlineData(5)] [InlineData(6)] public void MyFirstTheory(int value) { Assert.True(IsOdd(value)); } bool IsOdd(int value) { return value % 2 == 1; } }
[Theory, PropertyData("SplitCountData")] public void SplitCount(string input, int expectedCount) { var actualCount = input.Split(' ').Count(); Assert.Equal(expectedCount, actualCount); } public static IEnumerable<object[]> SplitCountData { get { return new[] { new object[] { "xUnit", 1 }, new object[] { "is fun", 2 }, new bject[] { "to test with", 3 } }; } }
[Fact(Skip="Trait Extensibility is not working in 1654"), Category("Slow Test")] public void LongTest() { Thread.Sleep(500); } [Fact, Trait("Category", "Supa")] public void LongTest2() { Assert.True(true); } [Fact(Timeout=50)] public void TestThatRunsTooLong() { System.Threading.Thread.Sleep(250); }
[TestMethod] public void IntroductoryTest() { // Fixture setup Fixture fixture = new Fixture(); int expectedNumber = fixture.Create<int>(); MyClass sut = fixture.Create<MyClass>(); // Exercise system int result = sut.Echo(expectedNumber); // Verify outcome Assert.AreEqual<int>(expectedNumber, result, "Echo"); // Teardown }
[Theory, AutoData] public void IntroductoryTest(int expectedNumber, MyClass sut) { int result = sut.Echo(expectedNumber); Assert.Equal(expectedNumber, result); }
Atrapy obiektów (mock objects) - symulowane obiekty naśladujące w kontrolowany sposób zachowanie rzeczywistych obiektów.
public class Client { // Wewnętrzna referencja do usługi private Service service; // Wstrzyknięcie przez konstruktor Client(Service service) { this.service = service; } // Metoda w której klient korzysta z usługi (chcemy wyizolować to działanie) public double Compute(double x) { return x * service.GetSomeValue(); } }
// Tworzymy obiekt atrapę var mockClient = new Mock<Service>(); // Ustawiamy metodę aby zwracała symulowaną wartość mockClient.Setup(mock => mock.GetSomeValue()).Returns(1); // Wstrzykujemy atrapę do obiektu który będzie testowany var client = new Clent(mockClient.Object); // Test Assert.IsEqual(client.Compute(2), oczekiwanaWartosc);
Pokrycie kodu mierzy, ile procent kodu zostało sprawdzone przez testy jednostkowego. Przyjmuje się, że dobrze napisane testy powinny mieć pokrycie rzędu przynajmniej 70% . Różne metryki pokrycia:
Testowanie mutacyjne – technika automatycznego badania, na ile dokładnie testy jednostkowe sprawdzają kod programu. Polega na automatycznym wielokrotnym wprowadzaniu małych losowych błędów w programie i uruchamianiu za każdym razem testów jednostkowych, które powinny te błędy wykryć. Testowanie mutacyjne wymaga dużej mocy obliczeniowej i dlatego dopiero od niedawna próbuje się je wykorzystywać w praktyce.
Źródło: pl.wikipedia.pl