Testowanie Entity Framework przy pomocy Effort

Niedawno pokazałem pierwsze próby testowania Entity Framework – a raczej tego czy serwis korzystający z jego DbContext zachowuje się tak jak tego byśmy chcieli. W testach użyliśmy biblioteki Moq, która pozwalała nam zasymulować DbContext i jego DbSet, wypełnić je danymi i przeprowadzić test. Jest jednak rozwiązanie pozwalające nam wierniej zasymulować nam realną bazę danych – poprzez utworzenie jej bezpośrednio w pamięci podczas wykonywania testów. Tym narzędziem jest biblioteka Effort i dziś pokażę jej użycie do przeprowadzenia prostego testu.

Jak to zwykle bywa do instalacji biblioteki posłużymy się NuGet Packet Managerem: wyszukujemy pakiet Effort.EF6 i instalujemy w naszym projekcie z testami.

Effort pozwoli nam na utworzenie wirtualnej bazy danych i wykorzystanie naszej klasy AppContext. Musimy jednak mieć możliwość utworzenia instancji AppContext z przekazaniem połączenia do bazy utworzonej przez Effort. W tym celu dodamy do klasy AppContext nowy konstruktor z parametrem typu DbConnection jak poniżej:

public AppContext(DbConnection connection)
: base(connection, true)
{ }

Następnie przejdźmy do naszej przykładowej klasy z testami serwisu MovieService. Poprzednio bazowaliśmy na obiektach symulowanych przez Moq. Po użyciu Effort nie są one już potrzebne – usuwamy je więc. Dodajemy również referencję do przestrzeni nazw Effort. Deklaracja prywatnych pól i konstruktor po zmianach wyglądają tak:

private AppContext appContext;
private MovieService movieService;

public MovieServiceTest()
{
    var connection = DbConnectionFactory.CreateTransient();
    appContext = new AppContext(connection);
    movieService = new MovieService(appContext);

}

Widzimy, że zamiast mockować nasz AppContext i jego DbSet<Movie>, który następnie wypełnialibyśmy danymi – tutaj tworzymy bazę danych w pamięci za pomocą metody DbConnectionFactory.CreateTransient(). Połączenie przekazujemy do konstruktora AppContext. Uzyskany w ten sposób obiekt posłuży nam do przeprowadzenia testu. Możemy bowiem korzystać z niego jak z „prawdziwej” bazy danych – czyli dodać nowe obiekty do kolekcji DbSet<Movie> i zapisać zmiany w bazie za pomocą metody SaveChanges(). Następnie sprawdzamy, czy testowana metoda GetAllMovies() zwraca prawidłową ilość rekordów:

[Fact]
public void GetAll_returns_all_movies()
{

    var data = new List<Movie>
    {
     new Movie { ID=1, Title = "Terminator" },
     new Movie { ID=2, Title = "Dead Poet Society" },
     new Movie { ID=3, Title = "Death in Venice" },
     new Movie { ID=4, Title = "Evil Dead 2" }
    }.AsQueryable();

    appContext.Movies.AddRange(data);
    appContext.SaveChanges();

    var resultSet = movieService.GetAllMovies();

    Assert.Equal(4, resultSet.Count());

}

Istnieje również inny sposób wczytania danych do wirtualnej bazy utworzonej przez Effort. Jeśli dysponujemy już danymi w fizycznej bazie danych – możemy je wyeksportować za pomocą narzędzia dostarczanego przez Effort – Effort CSV Export Tool. Pozwala ono na eksport danych do pliku csv, który będziemy mogli wczytać przy tworzeniu bazy danych Effort. Okno programu wygląda następująco:

007a

Dotychczas korzystaliśmy z bazy danych silnika LocalDB – by wyeksportować z niej dane jako Provider wybieramy SqlClient Data Provider a w polu ConnectionString wklejamy nasz connectionString z pliku App.config. Określamy również ścieżkę do folderu, w którym zostaną zapisane dane. Po wyeksportowaniu powinny w nim pojawić się pliki dla każdej z tabel, w której znajdują się dane.

Jak taki plik wczytać przed uruchomieniem testów? Do konstruktora bazy Effort musimy przekazać obiekt Loadera, który wczyta dane z naszych plików:

IDataLoader loader = new Effort.DataLoaders.CsvDataLoader(@"c:\db");
var connection = DbConnectionFactory.CreateTransient(loader);

I już możemy korzystać w testach z danych wczytanych do bazy. Nasz test może wtedy wyglądać tak:

[Fact]
public void GetAll_returns_all_movies()
{

    var resultSet = movieService.GetAllMovies();

    Assert.Equal(4, resultSet.Count());

}

Wydaje się to wygodne rozwiązanie gdy mamy już zgromadzoną pewną ilość danych i chcemy sprawdzić, czy nasze klasy działają na nich prawidłowo.

Będę korzystał z Effort przy następnych testach – takie rozwiązanie wydaje się być bliższe rzeczywistej bazie danych, a kod testów jest naturalniejszy i bliższy pracy na prawdziwym DbContext.

Marcin

Dodaj komentarz