JUnit i metody statyczne

0

Dlaczego nie można testować metod statycznych? Jedyne informacje jakie znalazłem to takie, żeby ich nie używac bo nie można testować, ale dlaczego ?

1

W jakim sensie nie można? Chodzi ci chyba o to, że nie można ich mockować (co jest możliwe z PowerMockiem).

1

Poza tym problem występuje wtedy, kiedy metody statyczne są furtką do jakiegoś stanu globalnego, tzn. gdzieś jest też na przykład zmienna statyczna. Tak być nie musi, ale może, bo jeśli ktoś powiedział już "A" decydując się na statyki, mogło mu się także wypsnąć "B".

Powstaje wtedy problem resetowania całego środowiska pomiędzy testami, ponieważ "zanieczyszczenia" pozostałe z jednego testu mogą zakłócać kolejny, prowadząc nawet do fenomenów tak nieprzyjemnych, że testy przechodzą albo wykładają się w zależności od kolejności, w jakiej są wywoływane. Innymi słowy to może zepsuć atomizację testów.

Jeśli natomiast metoda statyczna jest tylko bezstanową funkcją bez żadnych zależności - w stylu np. TextUtils.join - to nie tylko można, ale i należy ją testować, i nie powinno to powodować żadnych kłopotów.

3

mozna i jest to latwe gdy takowe metody nie zmieniaja zadnego stanu, nie jest problem napisac test do tego:

public static int add(int a, int b)  {
    return a + b;
}

z tym mozna tylko siasc i plakac:

public static void createSalesReport() {
   drawReportHeader();
   calculateEverything();
   if(error) {
        System.err.write(error);
        return;
   }
   sender.sendReportToAllClients();
   System.out.write(email);
}
2

Zarzut wobec staticów jest taki, że nie można ich "podmienić" czy to na mocka, czy na jakąś testową implementacje (pomijam powermocka, bo jak musisz go używać w swoim własnym kodzie to jest źle...). To niestety czasem mocno utrudnia testowanie bo nie zawsze możesz/chcesz robić test podpięty do wszystkiego. Jak puszczasz unit testy to chcesz zeby przeleciały w kilka sekund a nie mieliły godzinę, bo muszą jeździć łazikiem po placu (od tego są testy e2e czy integracyjne)

1
katelx napisał(a):

z tym mozna tylko siasc i plakac:

public static void createSalesReport() {
   drawReportHeader();
   calculateEverything();
   if(error) {
        System.err.write(error);
        return;
   }
   sender.sendReportToAllClients();
   System.out.write(email);
}

No tak - z tym że gdybyśmy mieli zamiast tego:

public void createSalesReport() {
   drawReportHeader();
   calculateEverything();
   if(error) {
        System.err.write(error);
        return;
   }
   sender.sendReportToAllClients();
   System.out.write(email);
}

To też nie rozwiązuje jeszcze problemu, którym są tak naprawdę "zasmażone" na twardo zależności :) Należałoby zrefaktorować całość w kierunku jakiegoś DI - usunięcie statyczności byłoby tylko jednym z etapów takiego refactoringu.

1

@V-2: no ale przeciez wlasnie taki jest glowny problem ze staticami (jesli chodzi o testowanie) ze maja zaleznosci :)

0

Jak to staticki mają zależności ? O co chodzi ?

1

@katelx pewno chodziło o to że mocka nie możesz zrobić :)

0

ale jak wyłumaczyć: mają zależności -> nie można zrobić mocka, wciąż brakuje jednego ogniwa

0

Zależności ustawione na sztywno mogą występować i bez statyków. A z kolei można sobie wyobrazić statyka, do którego się te zależności przekazuje (np. w parametrach metody). Jedno jest z drugim - owszem - skorelowane w tym sensie, że należy do proceduralnego stylu pisania programów i programiści lubiący statyki przeważnie nie widzą też nic złego w hardkodowaniu zależności :) Ale w sensie ścisłym jedno z drugiego nie musi wynikać.

0

Można testować metody statyczne i można je stosować. Ważne jest, żeby kod metody statycznej był całkowicie pure - to co zwraca metoda statyczna musi zależeć wyłącznie od parametrów tej metody.
Druga rzecz, to zależności:

  public static Instant tomorrow(){
      Clock clock = Clock.systemUTC(); 
      return clock.instant().plus(1, ChronoUnit.DAYS);
  }

Napisz asercję ze znaną w trakcie kompilacji wartością oczekiwaną - nie da się. Musiał byś zmienić metodę tak:

    public static Instant tomorrow(Clock clock){
        return clock.instant().plus(1, ChronoUnit.DAYS);
    }

Tylko to jest mało wygodne w użyciu, bo musisz przekazywać obiekt clock w każdym wywołaniu metody - prościej zrobić to tak:

public class TimeFactory {
  private final Clock clock;

  private TimeFactory(Clock clock) {
    this.clock = clock;
  }

  public static TimeFactory newUtcTimeFactory() {
    return new TimeFactory(Clock.systemUTC());
  }

  public static TimeFactory newTimeFactory(Clock clock) {
    return new TimeFactory(clock);
  }

  public Instant tomorrow() {
    return clock.instant().plus(1, ChronoUnit.DAYS);
  }

  public Instant yesterday() {
    return clock.instant().minus(1, ChronoUnit.DAYS);
  }

  // więcej metod opartych o ten sam zegar
}

Dzięki temu jesteś w stanie w testach wykorzystać coś takiego jak

public class TimeFactoryTest {
  @Test
  public void tomorrow() {
    Clock fixedClock = Clock.fixed(Instant.parse("2007-12-03T10:15:30.00Z"), ZoneId.of("UTC"));
    TimeFactory timeFactory = TimeFactory.newTimeFactory(fixedClock);
    Instant tomorrow = timeFactory.tomorrow();

    assertEquals(Instant.parse("2007-12-04T10:15:30.00Z"), tomorrow);
  }
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1