2009-05-30 10 views
8

Powszechne jest mieć zajęcia z metod z parametrami ciąg znaków, który musi zostać zatwierdzone agains null lub pusty, jak to przykładowo:Jak powinienem przetestować puste i/lub puste parametry łańcucha w metodzie?

public class MyClass { 

    public void MyMethod(string param){ 

     if(string.IsNullOrEmpty(param)){ 
      throw new ArgumentNullException(...); 
     } 
     //... 
    } 
} 

To jasne, że zachowanie tej metody jest taka sama dla obu (nieważne) wartości. Jest to bardzo częsta sytuacja, a jeśli chodzi o testowanie tych metod, zawsze mam wątpliwości, jak to zrobić. Zawsze utworzyć dwa oddzielne testy dla tych przypadków:

[TestClass] 
public class Tests { 

    [TestMethod] 
    public void MyMethod_should_fail_if_param_is_null(){ 
     //... 
     myclass.MyMethod(null); 
     //... 
    } 

    [TestMethod] 
    public void MyMethod_should_fail_if_param_is_empty(){ 
     //... 
     myclass.MyMethod(""); 
     //... 
    } 

}

Ale widzę zbyt dużo redundancję. Te testy są dokładnie takie same, z tą różnicą, że parametr jest przekazywany do metody. Bardzo mi to przeszkadza, ponieważ muszę utworzyć dwa testy dla każdego parametru łańcucha. Metoda z 3 parametrami miałaby tylko 6 testów, aby przetestować parametry.

Myślę, że to jest właściwy sposób testowania tych parametrów, ale jeśli wiem, że 99% parametrów ciągów zostanie sprawdzonych w ten sam sposób, czy nie byłoby lepiej po prostu przetestować je pod kątem zerowej (lub pustej) i założyć że zachowanie w drugim przypadku będzie takie samo?

Chciałbym wiedzieć, co o tym sądzisz. Wiem, że to, o co pytam, jest bardziej techniczną opinią niż techniczną, ale myślę, że społeczność testerów może mieć coś ciekawego do powiedzenia na temat tej sytuacji.

Dziękujemy!

Odpowiedz

11

Osobiście chciałbym rozważyć za pomocą jednego testu dla wszystkich parametrów. Nie jest to zgodne z normalnym dogmatem testów jednostkowych, ale zwiększa czytelność testów (minimalizując ilość kodu testowego, który jest poświęcony dość powtarzalnemu przypadkowi) i nie ma zbyt wiele wad. Tak, jeśli test się nie powiedzie, nie będziesz wiedział, czy wszystkie kontrole po pierwszym niepowodzeniu również się nie powiodą, ale czy to naprawdę problem?

Najważniejszą kwestią jest upewnienie się, że dysponujesz skrótem do przetestowania sprawy. Na przykład, można napisać coś takiego (jeżeli ramy testów jednostkowych nie ma go już):

public static void ExpectException<T>(Action action) where T : Exception 
{ 
    try 
    { 
     action(); 
     Assert.Fail("Expected exception " + typeof(T).Name); 
    } 
    catch (T exception) 
    { 
     // Expected 
    } 
} 

Następnie można napisać:

[Test] 
public void MyMethodFailsWithInvalidArguments() 
{ 
    ExpectException<ArgumentNullException>(() => myClass.MyMethod(null)); 
    ExpectException<ArgumentException>(() => myClass.MyMethod("")); 
} 

Znacznie bardziej zwięzły niż robi każdy z indywidualny blok try/catch, a nawet za pomocą atrybutu ExpectedException i wielu testów.

Możesz chcieć przeciążenia w przypadkach, w których chcesz również sprawdzić, czy w każdym przypadku nie zostały dotknięte żadne wyśmiane obiekty (w celu sprawdzenia, czy można uniknąć efektów ubocznych) lub ewentualnie przeciążenia z typowymi wyjątkami, takimi jak ArgumentNullException.

Dla metod pojedynczego parametru można nawet napisać metodę enkapsulacji dokładnie to, czego potrzebujesz:

public void ExpectExceptionForNullAndEmptyStrings(Action<string> action) 
{ 
    ExpectException<ArgumentNullException>(() => action(null)); 
    ExpectException<ArgumentException>(() => action("")); 
} 

następnie połączyć go z:

[Test] 
public void MyMethodFailsWithInvalidArguments() 
{ 
    // This *might* work without the 
    ExpectExceptionForNullAndEmptyStrings(myClass.MyMethod); 
} 

... a może jeszcze jeden do metod z pojedynczym parametrem, ale bez odwrotnego typu.

To jest prawdopodobnie będzie trochę daleko choć :)

+0

dziękuję za szybką odpowiedź! Stworzyłem już własną wersję ExpectException (możesz to sprawdzić tutaj http://gerardocontijoch.wordpress.com/2008/12/02/uso-de-expectedexceptionattribute-para-testear-excepciones/) i używać jej w tych przypadkach. Nie może bez niego żyć: P Podoba mi się proponowane przez ciebie rozwiązanie, myślałem o tym, ale jak powiedziałeś, "to nie jest zgodne z normalnym dogmatem testów jednostkowych" i chciałem tylko podążać za tym, wiesz , przyzwyczaić się do dobrych praktyk. Prawdopodobnie pójdę po to rozwiązanie. Zobaczmy, co inni sądzą ... –

+1

Chodzi o to, nie wierzę, że śledzenie dogmatu i rzucanie pragmatyzmu przez okno * jest * dobrą praktyką :) Warto * poznać * dogmat, ale potem świadomie go ignorować, kiedy "rozważyłem argumenty za i przeciw dla konkretnej sytuacji i stwierdziłem, że nie działa. –

+0

Całkowicie się z tobą zgadzam i to było powodem mojego pytania. Zauważyłem, że muszę postępować zgodnie z pewną "dobrą" praktyką, ale nie z wieloma profesjonalistami (rodzaj sprzeczności, dobra praktyka z większą ilością wad niż plusy), więc napisałem pytanie, aby sprawdzić, czy czegoś nie brakuje. –

-2

Jeśli używasz Java i JUnit można użyć tej składni

@Test(expected = IllegalArgumentException.class) 
public void ArgumentTest() { 
    myClass.MyMethod(""); 
    myClass.MyMethod(null); 
} 
+4

Ta metoda przejdzie, jeśli * co najmniej jeden * z tych wywołań metod wyrzuci właściwy wyjątek - zamiast, jeśli * oba *. Łatwo jest popełnić błąd - ten rodzaj testu jest przydatny (IMO) tylko w przypadku pojedynczego zdania. W przeciwnym razie nie sprawdzasz dokładnie, gdzie wyjątek został zgłoszony. –

+0

Tak, masz rację ... powinny być dwa testy ... – Janusz

Powiązane problemy