Framework xUnit daje możliwość posiadania wspólnego kontekstu danych dla całego Test Suite. Zostaną one stworzone tylko raz. Zobaczmy jak to zrobić.

 

Rys. 1. Solucja TestingPatterns

Do solucji TestingPatterns dodałem folder ClassFixture oraz plik Person.cs. Znajduje się w niej prosta klasa, którą wykorzystamy w testach:

public class Person
{
   public string Surname;
}

Dostarczeniem danych dla Test Suite zajmie się dodatkowa klasa o nazwie TestFixture. Dodałem ją do katalogu ClassFixture.

 

Rys. 2. Struktura solucji TestingPatterns po dodaniu pliku TestFixture.

Kod w pliku TestFixture.cs wygląda w następujący sposób:

using System.Collections.Generic;

namespace xUnitFeatures.ClassFixture
{
    public class TestFixture
    {
        public List<Person> Persons;
        public TestFixture()
        {
            Persons = new List<Person>;
            {
                new Person{Surname = "test"}
            };
        }
    }
}

Dane dla naszych testów są bardzo proste. Będziemy używać listy obiektów klasy Person. Znajduje się w niej tylko jeden obiekt.
Dodajmy teraz klasę z testami

 

Rys. 3. Struktura solucji TestingPatterns po dodaniu pliku Tests.

Do katalogu ClassFixture dodałem plik Tests.cs z zestawem testów:

using Xunit;

namespace xUnitFeatures.ClassFixture
{
    public class Tests : IClassFixture<TestFixture>
    {
        private readonly TestFixture _fixture;
        public Tests(TestFixture fixture)
        {
            _fixture = fixture;
        }

        [Fact]
        public void TestClear()
        {
            _fixture.Persons.Clear();
            var result = _fixture.Persons.Count;
            Assert.Equal(0, result);
        }

        [Fact]
        public void HaveCount()
        {
            var result = _fixture.Persons.Count;
            Assert.Equal(1,result);
        }
    }
}

Klasa Tests implementuje interfejs IClassFixture<T>. Konstruktor przyjmuje obiekt klasy TestFixture. Zostanie on tam wstrzyknięty przez framework xUnit. Przypisujemy go do prywatnej zmiennej _fixture, którą wykorzystujemy w testach. Konstruktor klasy Tests uruchomi się standardowo przed każdym testem. Jednak otrzyma on ten sam obiekt klasy TestFixture. Przez co kontekst danych dla wszystkich testów w Test Suite jest wspólny.

W teście o nazwie TestClear() wpływamy na źródło danych, czyszcząc listę o nazwie Persons. Przez wykorzystanie wspólnych danych istnieje ryzyko, niepowodzenia testu HaveCount(). Wszystko zależy od tego w jakiej kolejności zostaną one uruchomione przez Test Explorer lub inny test runner. Jeżeli oba testy przechodzą wystarczy je powielić zmieniając im nazwy i sprawdzić rezultat:

 

Rys. 4. Rezultat uruchomienia powielonych testów

W tym przykładzie wdać, że Test Explorer najpierw uruchomił test o nazwie TestClear() lub TestClear2() następnie HaveCount2(), na końcu HaveCount(), który został niezaliczony.

Podczas debugowania, możemy zobaczyć stan kolekcji, która znajduje się w obiekcie klasy TestFixture, przed wywołaniem testu HaveCount();

 

Rys. 5. Stan kolekcji Persons

Tworzenie Test Suit z wspólnymi danymi dla każdego z testów, łamie cechę Isolated akronimu FIRST. Więc należy bardzo uważać stosując wspólne dane między testami. Musimy mieć dobry powód do zastosowania tego podejścia.