Implementacja interfejsu IClassFixture sprawia, że mamy wspólny kontekst danych dla zestawu testów. Jest jednak sposób osiągnięcia tego efektu dla kilku zestawów testów.

Aby to zrobić potrzebujemy klasy fixture z atrybutem [CollectionDefinition(”nazwa”)] implementującą interfejs ICollectionFixture.

Skorzystamy z klasy Person stworzonej w poprzednim wpisie:

public class Person
{
   public string Surname;
}

Do solucji TestingPatterns dodałem nowy folder o nazwie CollectionFixture

 

Rys. 1. Struktura projektu

Stwórzmy teraz klasę fixture o nazwie TestGroupAFixture, w której będziemy mieć kolekcję obiektów klasy Person.

 

Rys. 2. Plik TestGroupAFixture.cs dodany do solucji

Zobaczmy jej kod:

[CollectionDefinition("Tests Group A")]
public class TestGroupAFixture : ICollectionFixture<TestGroupAFixture>
{
    public List<Person> Persons;

    public TestGroupAFixture()
    {
        Persons = new List<Person>
        {
            new Person{Surname = "test"}
        };
    }
}

TestGroupAFixture implementuje generyczny interface ICollectionFixture oraz posiada atrybut [CollectionDefinition()]. W konstruktorze tworzy listę obiektów typu Person z jednym elementem.
Dodajmy teraz pierwszy Test Suit do katalogu CollectionFixture o nazwie FirstTestClass

 

Rys. 3. Plik FirstTestClass.cs dodany do solucji

Zobaczmy kod:

[Collection("Tests Group A")]
public class FirstTestsClass
{
    readonly TestGroupAFixture _fixture;
    public FirstTestsClass(TestGroupAFixture fixture)
    {
        _fixture = fixture;
    }
    [Fact]
    public void TestHaveCount()
    {
        var result = _fixture.Persons.Count;
        Assert.Equal(1, result);
    }
    [Fact]
    public void TestClear()
    {
        _fixture.Persons.Clear();
        var result = _fixture.Persons.Count;
        Assert.Equal(1, result);
    }
}

Jak w przypadku implementacji interfejsu IClassFixture w konstruktorze przekazujemy obiekt klasy fixture, zostanie on tam wstrzyknięty przez framework xUnit. Przypisujemy go do prywatnej zmiennej _fixture, którą będziemy wykorzystywać w testach.
Dodajmy teraz drugi Test Suit do katalogu CollectionFixture o nazwie SecondTestClass:

 

Rys. 4. Plik SecondTestClass.cs dodany do solucji

Zobaczmy jej kod:

[Collection("Tests Group A")]
public class SecondTestsClass
{
    readonly TestGroupAFixture _fixture;
    public SecondTestsClass(TestGroupAFixture fixture)
    {
        _fixture = fixture;
    }
    [Fact]
    public void TestHaveCount()
    {
        var result = _fixture.Persons.Count;
        Assert.Equal(1, result);
    }
}

W klasie FirstTestClass test o nazwie TestClear() wpływa na źródło danych, czyści listę obiektów klasy Person. Po uruchomieniu testów z kolekcji, napotkamy problem test o nazwie TestHaveCount() z klasy SecondTestsClass() nie przechodzi:

 

Rys. 5. TestHaveCount() nie przechodzi

W tym przypadku testy wykonywały się w następującej kolejności:

  1. FirstTestClass TestHaveCount ()-> test przeszedł
  2. FirstTestClass TestClear() -> test przeszedł, wpłynął na dane
  3. SecondTestsClass TestHaveCoutnt() -> test nie przechodzi zmieniona kolekcja danych.

Oczywiście, gdy uruchomimy testy pojedynczo wszystkie przejdą.

Tworzenie kolekcji Test Suit z wspólnymi danymi, może korzystnie wpłynąć na czas wykonywania testów. Ponieważ dane będą tworzone tylko raz dla wszystkich Test Suit. Należy jednak uważać, aby żaden z testów nie modyfikował źródła danych, ponieważ może to negatywnie wpłynąć na inne testy. Stosowanie tego podejścia nie jest zgodne z cechą Isolated akronimu FIRST. Polecam więc z rozwagą z niego korzystać.