Sugerowałbym wyśmiewanie twoich połączeń z bazą danych. Mocks to w zasadzie obiekty, które wyglądają jak obiekt, do którego próbujesz wywołać metodę, w tym sensie, że mają te same właściwości, metody itp. Dostępne dla wywołującego. Ale zamiast wykonywać dowolną czynność, do której zostały zaprogramowane, gdy wywoływana jest określona metoda, całkowicie ją pomija i po prostu zwraca wynik. Wynik ten jest zwykle definiowany przez Ciebie z wyprzedzeniem.
Aby skonfigurować obiekty do mockowania, prawdopodobnie będziesz musiał użyć jakiegoś rodzaju odwrócenia wzorca iniekcji kontroli / zależności, jak w poniższym pseudokodzie:
class Bar
{
private FooDataProvider _dataProvider;
public instantiate(FooDataProvider dataProvider) {
_dataProvider = dataProvider;
}
public getAllFoos() {
// instead of calling Foo.GetAll() here, we are introducing an extra layer of abstraction
return _dataProvider.GetAllFoos();
}
}
class FooDataProvider
{
public Foo[] GetAllFoos() {
return Foo.GetAll();
}
}
Teraz w teście jednostkowym tworzysz makietę FooDataProvider, która umożliwia wywołanie metody GetAllFoos bez konieczności trafiania w bazę danych.
class BarTests
{
public TestGetAllFoos() {
// here we set up our mock FooDataProvider
mockRepository = MockingFramework.new()
mockFooDataProvider = mockRepository.CreateMockOfType(FooDataProvider);
// create a new array of Foo objects
testFooArray = new Foo[] {Foo.new(), Foo.new(), Foo.new()}
// the next statement will cause testFooArray to be returned every time we call FooDAtaProvider.GetAllFoos,
// instead of calling to the database and returning whatever is in there
// ExpectCallTo and Returns are methods provided by our imaginary mocking framework
ExpectCallTo(mockFooDataProvider.GetAllFoos).Returns(testFooArray)
// now begins our actual unit test
testBar = new Bar(mockFooDataProvider)
baz = testBar.GetAllFoos()
// baz should now equal the testFooArray object we created earlier
Assert.AreEqual(3, baz.length)
}
}
Powszechny scenariusz kpiny, w skrócie. Oczywiście nadal będziesz prawdopodobnie chciał przetestować jednostkowo również rzeczywiste wywołania bazy danych, dla których będziesz musiał trafić do bazy danych.
Idealnie byłoby, gdyby twoje obiekty były wytrwałymi ignorantami. Na przykład, powinieneś mieć „warstwę dostępu do danych”, do której będziesz wysyłać żądania, które zwracałyby obiekty. W ten sposób możesz pozostawić tę część poza testami jednostkowymi lub przetestować je w izolacji.
Jeśli obiekty są ściśle powiązane z warstwą danych, trudno jest przeprowadzić odpowiednie testy jednostkowe. pierwsza część testu jednostkowego to „jednostka”. Wszystkie jednostki powinny mieć możliwość testowania w izolacji.
W moich projektach C # używam NHibernate z całkowicie oddzielną warstwą danych. Moje obiekty znajdują się w podstawowym modelu domeny i są dostępne z mojej warstwy aplikacji. Warstwa aplikacji komunikuje się zarówno z warstwą danych, jak iz warstwą modelu domeny.
Warstwa aplikacji jest czasami nazywana „warstwą biznesową”.
Jeśli używasz PHP, utwórz określony zestaw klas TYLKO dla dostępu do danych. Upewnij się, że obiekty nie mają pojęcia, w jaki sposób są utrwalane, i połącz je w klasach aplikacji.
Inną opcją byłoby użycie mocking / stubs.
źródło
Najłatwiejszym sposobem testowania jednostkowego obiektu z dostępem do bazy danych jest użycie zakresów transakcji.
Na przykład:
Spowoduje to przywrócenie stanu bazy danych, w zasadzie jak wycofanie transakcji, dzięki czemu możesz uruchomić test tyle razy, ile chcesz, bez żadnych skutków ubocznych. Z powodzeniem stosowaliśmy to podejście w dużych projektach. Nasza kompilacja zajmuje trochę czasu (15 minut), ale posiadanie 1800 testów jednostkowych nie jest straszne. Ponadto, jeśli czas kompilacji jest problemem, możesz zmienić proces kompilacji, aby mieć wiele kompilacji, jedną do budowania src, drugą, która uruchamia się później, która obsługuje testy jednostkowe, analizę kodu, pakowanie itp.
źródło
Być może mogę wam posmakować naszego doświadczenia, kiedy zaczęliśmy przyglądać się testom jednostkowym naszego procesu średniego poziomu, który obejmował mnóstwo operacji SQL „logiki biznesowej”.
Najpierw stworzyliśmy warstwę abstrakcji, która pozwoliła nam „wstawić” dowolne rozsądne połączenie z bazą danych (w naszym przypadku po prostu obsługiwaliśmy pojedyncze połączenie typu ODBC).
Gdy to było na miejscu, mogliśmy zrobić coś takiego w naszym kodzie (pracujemy w C ++, ale jestem pewien, że masz pomysł):
GetDatabase (). ExecuteSQL ("INSERT INTO foo (bla, bla)")
W normalnym czasie wykonywania GetDatabase () zwróciłoby obiekt, który zasilał cały nasz plik sql (w tym zapytania), przez ODBC bezpośrednio do bazy danych.
Następnie zaczęliśmy przyglądać się bazom danych w pamięci - zdecydowanie najlepszym wydaje się być SQLite. ( http://www.sqlite.org/index.html ). Jest niezwykle prosty w konfiguracji i obsłudze, a także pozwolił nam podklasę i przesłonięcie GetDatabase () w celu przekazania sql do bazy danych w pamięci, która została utworzona i zniszczona dla każdego wykonywanego testu.
Wciąż jesteśmy na wczesnym etapie, ale jak na razie wygląda to dobrze, jednak musimy upewnić się, że tworzymy wszystkie wymagane tabele i wypełniamy je danymi testowymi - jednak nieco zmniejszyliśmy obciążenie tutaj, tworząc ogólny zestaw funkcji pomocniczych, które mogą wiele zdziałać za nas.
Ogólnie rzecz biorąc, bardzo pomogło to w naszym procesie TDD, ponieważ wprowadzenie czegoś, co wydaje się dość nieszkodliwymi zmianami w celu naprawienia niektórych błędów, może mieć dość dziwny wpływ na inne (trudne do wykrycia) obszary twojego systemu - ze względu na samą naturę sql / baz danych.
Oczywiście nasze doświadczenia skupiały się wokół środowiska programistycznego C ++, jednak jestem pewien, że być może możesz uzyskać coś podobnego działającego w PHP / Python.
Mam nadzieję że to pomoże.
źródło
Powinieneś mockować dostęp do bazy danych, jeśli chcesz przetestować swoje klasy jednostkowo. W końcu nie chcesz testować bazy danych w teście jednostkowym. To byłby test integracji.
Odebrać wywołania, a następnie wstawić makietę, która po prostu zwraca oczekiwane dane. Jeśli twoje klasy nie robią więcej niż wykonywanie zapytań, może nawet nie warto ich testować, chociaż ...
źródło
W książce xUnit Test Patterns opisano niektóre sposoby obsługi kodu testów jednostkowych, który trafia do bazy danych. Zgadzam się z innymi ludźmi, którzy mówią, że nie chcesz tego robić, ponieważ jest to powolne, ale musisz to kiedyś zrobić, IMO. Wyszydzenie połączenia db w celu przetestowania rzeczy wyższego poziomu jest dobrym pomysłem, ale przejrzyj tę książkę, aby uzyskać sugestie dotyczące interakcji z rzeczywistą bazą danych.
źródło
Dostępne opcje:
Wstrzyknij bazę danych. (Przykład w pseudo-Java, ale dotyczy wszystkich języków OO)
teraz w środowisku produkcyjnym używasz normalnej bazy danych i dla wszystkich testów po prostu wstrzykujesz fałszywą bazę danych, którą możesz utworzyć ad hoc.User
zamiast krotki{name: "marcin", password: "blah"}
) napisz wszystkie testy z rzeczywistymi obiektami skonstruowanymi ad hoc i napisz jeden duży test, który zależy od bazy danych, która zapewnia taką konwersję działa OK.Oczywiście te podejścia nie wykluczają się wzajemnie i można je dowolnie łączyć i dopasowywać.
źródło
Testowanie jednostkowe dostępu do bazy danych jest dość łatwe, jeśli projekt ma wysoką spójność i luźne powiązania. W ten sposób możesz przetestować tylko to, co robi każda klasa, bez konieczności testowania wszystkiego naraz.
Na przykład, jeśli wykonujesz testy jednostkowe klasy interfejsu użytkownika, napisane przez Ciebie testy powinny tylko próbować zweryfikować, czy logika wewnątrz interfejsu użytkownika działa zgodnie z oczekiwaniami, a nie logika biznesowa lub akcja bazy danych za tą funkcją.
Jeśli chcesz przetestować jednostkowo rzeczywisty dostęp do bazy danych, skończysz bardziej testem integracji, ponieważ będziesz zależny od stosu sieciowego i serwera bazy danych, ale możesz sprawdzić, czy twój kod SQL robi to, o co go prosiłeś robić.
Dla mnie osobiście ukryta moc testów jednostkowych polega na tym, że zmusza mnie to do projektowania aplikacji w znacznie lepszy sposób niż bez nich. To dlatego, że naprawdę pomogło mi to oderwać się od mentalności „ta funkcja powinna zrobić wszystko”.
Przepraszam, nie mam żadnych konkretnych przykładów kodu dla PHP / Python, ale jeśli chcesz zobaczyć przykład .NET, mam post opisujący technikę, której użyłem do wykonania tych samych testów.
źródło
Zwykle próbuję zrywać testy między testowaniem obiektów (i ORM, jeśli istnieje) i testowaniem bazy danych. Testuję obiektową stronę rzeczy, mockując wywołania dostępu do danych, podczas gdy testuję stronę db, testując interakcje obiektu z bazą danych, która, z mojego doświadczenia, jest zwykle dość ograniczona.
Kiedyś frustrowałem się pisaniem testów jednostkowych, dopóki nie zacząłem kpić z części dostępu do danych, więc nie musiałem tworzyć testowej bazy danych ani generować danych testowych w locie. Mockując dane, możesz wygenerować je wszystkie w czasie wykonywania i mieć pewność, że obiekty działają poprawnie ze znanymi danymi wejściowymi.
źródło
Nigdy nie robiłem tego w PHP i nigdy nie używałem Pythona, ale to, co chcesz zrobić, to wyszydzenie wywołań do bazy danych. Aby to zrobić, możesz zaimplementować IoC niezależnie od tego, czy jest to narzędzie innej firmy, czy zarządzasz nim samodzielnie, a następnie możesz zaimplementować próbną wersję programu wywołującego bazę danych, dzięki której będziesz kontrolować wynik fałszywego połączenia.
Prosta forma IoC może być wykonana po prostu przez kodowanie do interfejsów. Wymaga to pewnej orientacji obiektowej w kodzie, więc może nie mieć zastosowania do tego, co robisz (mówię to, ponieważ wszystko, co muszę zrobić, to twoja wzmianka o PHP i Pythonie)
Mam nadzieję, że to pomocne, jeśli nie masz teraz żadnych terminów do wyszukania.
źródło
Zgadzam się z pierwszym po - dostęp do bazy danych powinien zostać usunięty do warstwy DAO, która implementuje interfejs. Następnie możesz przetestować swoją logikę pod kątem implementacji kodu pośredniczącego warstwy DAO.
źródło
Możesz użyć mocking frameworków, aby wyodrębnić silnik bazy danych. Nie wiem, czy PHP / Python ma jakieś, ale dla języków typowanych (C #, Java itp.) Jest wiele opcji
Zależy to również od tego, jak zaprojektowałeś kod dostępu do bazy danych, ponieważ niektóre projekty są łatwiejsze do testowania jednostkowego niż inne, o których wspominano we wcześniejszych postach.
źródło
Konfigurowanie danych testowych dla testów jednostkowych może być wyzwaniem.
Jeśli chodzi o Javę, jeśli używasz Spring API do testów jednostkowych, możesz kontrolować transakcje na poziomie jednostki. Innymi słowy, można wykonywać testy jednostkowe, które obejmują aktualizacje / wstawienia / usunięcia bazy danych oraz wycofywanie zmian. Pod koniec wykonywania pozostawiasz wszystko w bazie danych tak, jak było przed rozpoczęciem wykonywania. Dla mnie jest tak dobry, jak to tylko możliwe.
źródło