Problem występuje podczas wykonywania TDD. Po kilku zdaniach testowych zmieniają się typy zwracane przez niektóre klasy / moduły. W statycznym języku programowania, jeśli poprzedni próbny obiekt był używany w testach innej klasy i nie został zmodyfikowany w celu odzwierciedlenia zmiany typu, wystąpią błędy kompilacji.
Jednak w przypadku języków dynamicznych zmiana typów zwracanych znaków może nie zostać wykryta, a testy drugiej klasy będą nadal zaliczane. Jasne, że mogą istnieć testy integracyjne, które później powinny zakończyć się niepowodzeniem, ale testy jednostkowe zakończyłyby się błędem. Czy jest jakiś sposób, aby tego uniknąć?
Aktualizowanie za pomocą trywialnej próbki (w niektórych wymyślonych językach) ...
Wersja 1:
Calc = {
doMultiply(x, y) {return x * y}
}
//.... more code ....
// On some faraway remote code on a different file
Rect = {
computeArea(l, w) {return Calc.doMultipy(x*y)}
}
// test for Rect
testComputeArea() {
Calc = new Mock()
Calc.expect(doMultiply, 2, 30) // where 2 is the arity
assertEqual(30, computeArea)
}
Teraz w wersji 2:
// I change the return types. I also update the tests for Calc
Calc = {
doMultiply(x, y) {return {result: (x * y), success:true}}
}
... Następnie Rect zgłasza wyjątek w czasie wykonywania, ale test nadal się powiedzie.
źródło
class X
, ale testyclass Y
zależą od tego,X
a zatem są testowane na podstawie innego kontraktu niż to, z czym ma do czynienia w produkcji.Odpowiedzi:
W pewnym stopniu jest to tylko część kosztów prowadzenia działalności w dynamicznych językach. Otrzymujesz dużą elastyczność, znaną również jako „wystarczająca ilość liny do zawieszenia się”. Uważaj na to.
Dla mnie problem sugeruje użycie innych technik refaktoryzacji niż w przypadku języka o typie statycznym. W języku statycznym częściowo zastępujesz typy zwracane, abyś mógł „polegać na kompilatorze”, aby znaleźć miejsca, które mogą ulec uszkodzeniu. Jest to bezpieczne i prawdopodobnie bezpieczniejsze niż modyfikowanie typu zwrotu w miejscu.
W dynamicznym języku nie możesz tego zrobić, więc bezpieczniej jest zmodyfikować typ zwracanego miejsca niż go zastąpić. Być może zmodyfikujesz go, dodając do niego nową klasę dla klas, które tego potrzebują.
źródło
W przypadku zmiany kodu, a jeszcze przejść testy, potem jest albo coś nie tak z badań (tj brakuje twierdzenie), czy kod nie faktycznie zmienić.
Co mam przez to na myśli? Twoje testy opisują umowy między częściami twojego kodu. Jeśli kontraktem jest „wartość zwracana musi być iterowalna”, wówczas zmiana wartości zwracanej z powiedzenia tablicy na listę nie jest w rzeczywistości zmianą umowy, a zatem niekoniecznie spowoduje niepowodzenie testu.
W celu uniknięcia brakujących twierdzeń, można użyć narzędzi, takich jak analiza pokrycia kodu (nie powie, jakie części kodu są testowane, ale to będzie powiedzieć, jakie części na pewno nie są testowane), cyclomatic złożoności i skomplikowania NPath (co daje dolną granicę ilości twierdzeń wymagany do osiągnięcia pełnej C1 i C2 pokrycia kodu) oraz mutacji testerów (który wprowadza mutacje w kodzie, takie jak toczenie
true
sięfalse
, liczb ujemnych na dodatnie, przedmiotów donull
itd. i sprawdzić, czy powoduje, że testy kończą się niepowodzeniem).źródło