Respawn – usuwanie danych z bazy

Wprowadzenie

W poprzednim wpisie pokazałem Ci, jak użyć Sql Server Snapshots do resetowania stanu bazy w automatycznych testach. Innym podejściem, które możemy wykorzystać podczas pracy, jest po prostu usuwanie z bazy danych, które zostały dodane podczas wykonywania testów. Tytułowa biblioteka Respawn umożliwia realizację czegoś takiego w bardzo prosty sposób, ale ma również niestety swoje wady. Zobacz, co umożliwia i gdzie może Ci się ona przydać.

Respawn

Respawn (https://github.com/jbogard/Respawn) jest prostą biblioteką, która ułatwia usuwanie danych. Jak podaje sam autor biblioteki, jest to inteligentne narzędzie do oczyszczania bazy danych do testów integracyjnych. „Inteligentne” oznacza tutaj, że biblioteka w pierwszej kolejności analizuje schemat bazy danych, aby na jej podstawie zdecydować o kolejności usuwania danych z bazy. Dzięki temu nie musimy na przykład zdejmować ograniczeń dla kluczy obcych w bazie, ponieważ biblioteka w pierwszej kolejności usunie rekordy zależne, a później rekord główny.

Już tutaj widać potencjalnie największy problem biblioteki, który może zadecydować, czy postanowisz jej użyć, czy nie. Jest nim to, że biblioteka usuwa wszystkie dane z tabel, które nas interesują. Jest to duża różnica w stosunku do migawek ze Sql Servera – tam wracaliśmy do określonego stanu bazy, gdzie w tabelach mogły znajdować się wcześniej przygotowane dane. W przypadku Respawn reset bazy oznacza po prostu usunięcie danych z tabel.

Darmowy kurs Visual Studio

Pracując z setkami programistów, zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio – aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 30 lekcji e-mail, w których pokażę Ci, w jaki sposób pracować efektywniej i szybciej w Visual Studio. Poznasz dodatki, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Respawn w akcji

Na potrzeby dzisiejszego wpisu przygotowałem prostą aplikację konsolową (https://github.com/danielplawgo/RespawnTests), w której znajduje się prosta struktura bazy z relacją jeden do wielu oraz wiele do wielu. Użyłem Entity Framework z podejściem Code First, a w aplikacji znajdują się następujące klasy modelu:

public class BaseModel
{
public BaseModel()
{
IsActive = true;
}
public int Id { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedUser { get; set; }
public DateTime UpdatedDate { get; set; }
public string UpdatedUser { get; set; }
}
public class Category : BaseModel
{
public string Name { get; set; }
}
public class Book : BaseModel
{
public string Title { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<Person> Authors { get; set; }
}
public class Person : BaseModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
view raw Models.cs hosted with ❤ by GitHub

Klasa BaseModel zawiera podstawowe właściwości, które chce mieć w każdej klasie modelu. Klasa Category jest powiązana relacją jeden do wielu z klasą Book, a następnie klasa Book jest powiązana relacją wiele do wielu z klasą Person.

W przykładzie dodałem jeszcze prostą metodę SeedData, która wrzuca do bazy dane wygenerowane przez bibliotekę NBuilder. W późniejszym kroku dane te będą usuwane przez Respawn. Tutaj we wpisie nie będę już wklejał kodu tej metody, bo nie jest on istotny w kontekście omawiania biblioteki Respawn.

Samo użycie biblioteki Respawn jest dość proste i wygląda tak:

class Program
{
private static Checkpoint _checkpoint = new Checkpoint
{
TablesToIgnore = new[]
{
"__MigrationHistory"
}
};
static void Main(string[] args)
{
SeedData();
_checkpoint.Reset(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString).Wait();
}
}
view raw Program.cs hosted with ❤ by GitHub

Na początku definiujemy instancję klasy Checkpoint, w której określamy konfigurację. W kodzie widać najczęściej używaną właściwość TablesToIgnore, w której określamy tabele, z których biblioteka nie usunie danych podczas resetu bazy. W moim przykładzie nie chcę, aby biblioteka usuwała dane z tabeli z migracjami Entity Framework.

Samo usunięcie danych odbywa się poprzez wywołanie metody Reset na obiekcie klasy Checkpoint i przekazaniu connection stringa do bazy w parametrze metody. Warto zauważyć, że metoda jest asynchroniczna (zwraca obiekt klasy Task), więc w tym przypadku dodałem jeszcze wywołanie metody Wait.

W efekcie wykonywania powyższego kodu Respawn wygenerowało i wykonało takiego sqla usuwającego dane z bazy:

DELETE "dbo"."PersonBooks";
DELETE "dbo"."People";
DELETE "dbo"."Books";
DELETE "dbo"."Categories";
view raw delete.sql hosted with ❤ by GitHub

Widać, że kolejność usuwania danych jest poprawna. W tym sensie, że najpierw usuwane są dane z tabeli PersonBooks, która jest elementem relacji wiele do wielu, a na samym końcu są usuwane dane z tabeli Categories, która nie zależy od innych tabel.

Natomiast pobranie metadanych o bazie tuż przed usunięciem danych wykonało takiego sqla:

select
fk_schema.name, so_fk.name,
pk_schema.name, so_pk.name,
sfk.name
from
sys.foreign_keys sfk
inner join sys.objects so_pk on sfk.referenced_object_id = so_pk.object_id
inner join sys.schemas pk_schema on so_pk.schema_id = pk_schema.schema_id
inner join sys.objects so_fk on sfk.parent_object_id = so_fk.object_id
inner join sys.schemas fk_schema on so_fk.schema_id = fk_schema.schema_id
where 1=1 AND so_pk.name NOT IN (N'__MigrationHistory')
view raw metadata.sql hosted with ❤ by GitHub

Konfiguracja Respawn

Poza pokazanym wcześniej użyciem właściwości TablesToIgnore możemy ustawić w klasie Checkpoint jeszcze kilka innych właściwości:

  • TablesToInclude – ręczne określenie tabel, z których ma nastąpić usunięcie danych,
  • SchemasToInclude oraz SchemasToExclude – określamy usunięcie danych lub nie na podstawie schematu w bazie,
  • WithReseed – resetuje autonumerowanie dla kluczy w tabelach, dzięki czemu dodanie nowych rekordów do bazy po resecie spowoduje, że klucze będą ponownie generowane od wartości startowej (na ogół 1).

Myślę, że na ogół najsensowniejszą opcją jest skorzystanie z dwóch właściwości. W TablesToIgnore określamy tabele, z których nie chcemy usuwać danych (np. tabele z migracjami Entity Framework). Drugą właściwością jest WithReseed ustawione na true, aby dodawane rekordy do bazy miały zawsze ten sam klucz.

Przykład

Na githubie (https://github.com/danielplawgo/RespawnTests) już tradycyjnie znajdziesz przykład, którego użyłem do pracy nad tym wpisem. Po jego pobraniu należy w app.config ustawić connection stringa do testowej bazy danych.

Podsumowanie

Respawn jest ciekawą alternatywą dla Sql Server Snapshots w kwestii resetowania bazy do znanego stanu. Jak podaje sam autor (https://jimmybogard.com/respawn-vs-sql-server-snapshots/), użycie Respawn jest dużo bardziej efektywne (testy wykonują się szybciej) niż użycie migawek. Więc w sytuacji, gdy czyszczenie danych nie jest problemem w Twoich scenariuszach testowych, myślę, że Respawn jest ciekawą alternatywą.

Osobiście obecnie zostaję przy użyciu migawek. W swoich testach WebApi bardzo często zakładam, że w bazie na starcie znajdują się określone dane i użycie Respawna bardzo by mi utrudniło pisanie testów. Jednak próbuję pozmieniać kod samej biblioteki Respawn, który jest na githubie, aby bardziej selektywnie usuwać dane. Jak coś ciekawego mi z tego wyjdzie, to z chęcią się kiedyś podzielę tym na blogu.

Szkolenie Automatyczne testy w .NET 5

SzkolenieAutomatyczne testy w .NET 5

Zainteresował Ciebie ten temat? A może chcesz więcej? Jak tak to zapraszam na moje autorskie szkolenie o automatycznych testach w .NET..

4 thoughts on “Respawn – usuwanie danych z bazy

  • Pingback: dotnetomaniak.pl
  • A nie latwiej po prostu uruchomic test w transakcji (TestInitialize) i na koniec zrobic rollback (TestCleanup) ?
    Jakos nie za bardzo wyobrazam sobie ta biblioteke + duze bazy danych.

    • Hej Piotr,

      Jak najbardziej można tak to zrobić, wszystko zależy jakie testy tworzysz i jak są uruchamiane. Sam autor Respawn w jednym z swoich wpisów na blogu porównuje różne strategie podejścia do tego problemu, w tym również używanie transakcji – https://lostechies.com/jimmybogard/2013/06/18/strategies-for-isolating-the-database-in-tests/

      Tak jak napisałem w podsumowaniu, osobiście korzystam z migawek Sql Servera do moich testów WebApi z Postmana. Ostatnio trafiłem na tą bibliotekę, pobawiłem się nią trochę i pomyślałem, że może komuś ona podpasuje do jego potrzeb, dlatego powstał ten wpis 🙂

      Dzięki za komentarz!

  • Dzięki za wpis. Całkiem niedawno w projekcie rozważaliśmy czego użyć do czyszczenia bazy SQL Server po skończonych automatycznych testach UI. Pierwszy wybór padł na snapshoty. Jednak szybko okazało się, że Azure SQL tego nie wspiera. Ostatecznie napisaliśmy skrypt SQL, który robi truncate na odpowiednich tabelach. Jednak na wspomnianą bibliotekę Respawn nie trafiliśmy, wiec dzieki za wpis – może przyda się w przyszłości 🙂

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.