2015-02-27 13 views
5

Powiedzmy masz następujące klasy chciałbyś Test:Najlepsza praktyka - Ustawienie pola bez ustawiaczy w badanej jednostki

public class SomeService { 
    public String someMethod(SomeEntity someEntity) { 
    return someEntity.getSomeProperty(); 
    } 
} 

SomeEntity wygląda następująco:

public class SomeEntity { 
    private String someProperty; 

    public getSomeProperty() { 
    return this.someProperty; 
    } 
} 

Twierdzenie chciałbyś zrobić:

String result = someService.someMethod(someEntity); 

assertThat(result).isEqualTo("someValue"); 

Jak możesz sprawić, aby ten test zadziałał?

1) Dodaj setera dla "someProperty" w klasie SomeEntity. Nie wydaje mi się to dobrym rozwiązaniem, ponieważ nie zmieniasz kodu produkcyjnego, aby testy działały.

2) Użyj ReflectionUtils, aby ustawić wartość tego pola. Test będzie wyglądać następująco:

public class TestClass { 
     private SomeService someService; 

     @Test 
      public void testSomeProperty() { 
      SomeEntity someEntity = new SomeEntity(); 
      ReflectionTestUtils.setField(someEntity, "someProperty", "someValue"); 

      String result = someService.someMethod(someEntity); 

      assertThat(result).isEqualTo("someValue"); 
      } 
} 

3) utworzyć klasę wewnętrzną w swojej klasie testowej, która rozszerza klasę SomeEntity i dodaje setter dla tej dziedzinie. Jednak aby to działało, musisz również zmienić klasę SomeEntity, ponieważ pole powinno stać się "chronione" zamiast "prywatne". Klasa testowa może wyglądać następująco:

public class TestClass { 
    private SomeService someService; 

    @Test 
    public void testSomeProperty() { 
    SomeEntityWithSetters someEntity = new SomeEntityTestWithSetters(); 
    someEntity.setSomeProperty("someValue"); 

    String result = someService.someMethod(someEntity); 

    assertThat(result).isEqualTo("someValue"); 
    } 


    public class SomeEntityWithSetters extends SomeEntity { 
    public setSomeProperty(String someProperty) { 
    this.someProperty = someProperty; 
    } 
    } 
} 

4) Używasz Mockito do kpiny SomeEntity. Wydaje się być w porządku, jeśli potrzebujesz tylko wyśmiać tylko jedną właściwość w klasie, ale co, jeśli musisz wyśmiać jak 10 właściwości. Test może wyglądać następująco:

public class TestClass { 
    private SomeService someService; 

    @Test 
    public void testSomeProperty() { 
    SomeEntity someEntity = mock(SomeEntity.class); 
    when(someEntity.getSomeProperty()).thenReturn("someValue"); 

    String result = someService.someMethod(someEntity); 

    assertThat(result).isEqualTo("someValue"); 
    } 
} 
+1

jeśli klasa ClassToTest doesent ma jakiegoś konstruktora lub setera .. jak w prod oni zapełniają nieruchomość .. ponownie z odbiciem? –

+0

i domyślam się, że masz na sobie testową metodę SomeService, a nie Classtotest. jeśli to jest przypadek, możesz kpić z ClasstoTest. testowanie ClasstoTest będzie wykonane osobno i nie powinno się tam kpić z niego. –

+0

W tym przypadku właściwość została ustawiona przez Hibernate, jest to baza danych Entity, Hibernate nie potrzebuje ustawiania, aby to zrobić. – wvp

Odpowiedz

0

Z testów junit z SomeService.someMethod()

alternatywa 1. Nie należy używać tego jako bez konieczności zmiany podmiotu do pisania JUnit.

alternatywa 2. może być używana.

alternatywa 3. znowu to samo, 3, nie trzeba przedłużać tylko dla junit. o tym, kiedy klasa nie może zostać przedłużona.

alternatywa 4. tak, dobra opcja. mockito jest używane z tego samego powodu.

+0

To jest tylko prosty przykład, someMethod() prawdopodobnie zrobi trochę więcej rzeczy, ale jest to tylko ilustracja problemu – wvp

+0

tak, prawda, a jeśli jakiś Metodą wzywa do SomeEntity, które powinny być wyśmiewane. w ten sposób testowany będzie someMethod. dobrze ? –

+0

Moim zdaniem rzeczywiście powinno się wyśmiewać, jeśli niektóreEntity byłoby zależne od tej klasy, a nie jeśli jest to obiekt, którego używasz. Usługa, repozytorium, program odwzorowujący ... takie rzeczy muszą być naprawdę wyśmiewane. – wvp

2

Można dodać program ustawiający z domyślnym zakresem (pakiet prywatny).

+0

Zmieniłoby to również kod produkcyjny, nie chcę tego robić, jeśli to możliwe – wvp

+5

Rozwój oparty na testach alwasy ma duży wpływ na sposób kodowania. Często poprawia to kod. Ale czasami narusza ukrywanie informacji, aby informacje były widoczne dla testów. Nie możesz mieć doskonałego zasięgu testowego i ukrywania idealnych informacji. – BetaRide

0

Jakie zachowanie/kontrakt specyficzne dla SomeService można przetestować? W oparciu o twój szkieletowy kod, naprawdę nie ma żadnych. Spowoduje to albo wyrzucenie NPE na złe dane wejściowe, albo zwrócenie ciągu znaków, który może być lub nie być pusty, w zależności od magii Hibernacji. Nie wiesz, co naprawdę możesz przetestować.

0

Doświadczyłem już wiele razy tego samego dylematu, szybkie rozwiązanie polega na tym, aby pole, na którym chcesz sfałszować pakiet, było chronione, lub zapewnić chronionego setera. Oczywiście oba zmienią kod produkcyjny.

Alternatywnie można rozważyć strukturę zależności wtrysku, taką jak Dagger.Poniżej znajduje się przykład dają:

Test
@Module 
class DripCoffeeModule { 
    @Provides Heater provideHeater(Executor executor) { 
    return new CpuHeater(executor); 
    } 
} 

ta nadpisuje DripCoffeeModule JUnit wiązanie za zbiornikiem z makiety obiektu z Mockito. Maniak zostaje wstrzyknięty do CoffeeMakera, a także do testu.

public class CoffeeMakerTest { 
    @Inject CoffeeMaker coffeeMaker; 
    @Inject Heater heater; 

    @Before public void setUp() { 
    ObjectGraph.create(new TestModule()).inject(this); 
    } 

    @Module(
     includes = DripCoffeeModule.class, 
     injects = CoffeeMakerTest.class, 
     overrides = true 
) 
    static class TestModule { 
    @Provides @Singleton Heater provideHeater() { 
     return Mockito.mock(Heater.class); 
    } 
    } 

    @Test public void testHeaterIsTurnedOnAndThenOff() { 
    Mockito.when(heater.isHot()).thenReturn(true); 
    coffeeMaker.brew(); 
    Mockito.verify(heater, Mockito.times(1)).on(); 
    Mockito.verify(heater, Mockito.times(1)).off(); 
    } 
} 
5

można ustawić wartość przy użyciu odbicia. Nie wymaga żadnych zmian w kodzie produkcyjnym.

ReflectionTestUtils.setField (YourClass.class, "fieldName", fieldValue);

Powiązane problemy