Tworzenie zestawów danych w testach jednostkowych razem z mockito. Stubbować gettery czy tworzyć instancje?

0

Cześc.
Mockito 2 dużo bardziej wymusza pisanie dokładnych testów i nie akceptuje juz tak nulli w matcherach jak wcześniej, w związku z czym trzeba bardziej dbać o zapewnianie pełnego zbioru danych testowych dla danego przypadku. Nie wiem jaka jest lepsza praktyka w przypadku kiedy wykorzystujemy jakieś zwykłe pojo, które ma sporo pól ( np 7 albo więcej ) oraz jedynie konstruktor ustawiający je wszystkie, tj. nie ma domyślnego. Pojo zazwyczaj przekazywane jako argument metody i wyciągane są z niego potrzebne dane, ale nie wszystkie. Na przykład w danym teście potrzebujemy tylko aby miało wypełnione 2 pola z 7 co lepiej wtedy robić"

a) Instancja obiektu z nullami w miejscu niepotrzebnych danych?:

new PersonDTO ("Dżoł", null, 22,null, null, null, null);

To jest lajtowy przykład, zdażają się naprawde grube obiekty.

b) Dodanie bezparametrowego konstruktora na potrzeby testu i zasetowanie potrzebnych pól?

c) Zmockowanie obiektu i stubowanie getterów ?

PersonDTO dto = mock(PersonDTO.class);
given(dto.getName).willReturn("Dżoł);
given(dto.getAge).willReturn(22);

Tak jak pisałem, to jest raczej lajtowy przykład, ale zdażają się przypadki, że jest 15 pól a potrzebne tylko 3 w teście.

0

d) Stworzenie zestawu fabryk na potrzeby testów.

0

Są jakieś przykłady, jak by to miało wyglądać? Zwykłe metody fabrykujące? Wiele to nie zmienia bo to jest przypadek a) tylko wyabstrachowany do metody czy klasy. Czesto przypadki sa takie, że to 1 test jest tylko taki, więc "zestaw fabryk" brzmi bardzo grubo

1

Żeby nie było. Pomijając nulle, to nie uważam, że podejście a) jest złe. Jest po prostu upierdliwe i trochę gorzej, kiedy model się zmienia drastycznie. Co do fabryk to można zrobić to np. tak. O ile to trochę kodu na start, to potem łatwiej w testach. Poza tym nie jest ciężko napisać jakiś procesor adnotacyjny, który by wygenerował to za nas.

final class AddressDto {
  final String street;
  final String zipCode;
  final String country;

  AddressDto(String street, String zipCode, String country) {
    this.street = street;
    this.zipCode = zipCode;
    this.country = country;
  }
}

final class UserDto {
  final String firstName;
  final String lastName;
  final int age;
  final AddressDto address;

  UserDto(String firstName, String lastName, int age, AddressDto address) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
    this.address = address;
  }
}

final class AddressDtoTestFactory {
  private final String street;
  private final String zipCode;
  private final String country;

  private AddressDtoTestFactory(String street, String zipCode, String country) {
    this.street = street;
    this.zipCode = zipCode;
    this.country = country;
  }
  
  static AddressDtoTestFactory createDefault() {
    return new AddressDtoTestFactory("Street", "ZipCode", "Country");
  }

  AddressDto createAddress() {
    return new AddressDto(street, zipCode, country);
  }

  AddressDtoTestFactory withStreet(String street) {
    return new AddressDtoTestFactory(street, zipCode, country);
  }
}

final class UserDtoTestFactory {
  private final String firstName;
  private final String lastName;
  private final int age;
  private final AddressDtoTestFactory addressFactory;
  
  private UserDtoTestFactory(String firstName, String lastName, int age, AddressDtoTestFactory addressFactory) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
    this.addressFactory = addressFactory;
  }
  
  static UserDtoTestFactory createDefault() {
    return new UserDtoTestFactory("FirstName", "LastName", 0, AddressDtoTestFactory.createDefault());
  }
  
  UserDto createUser() {
    return new UserDto(firstName, lastName, age, addressFactory.createAddress());
  }
  
  UserDtoTestFactory withAge(int age) {
    return new UserDtoTestFactory(firstName, lastName, age, addressFactory);
  }
  
  UserDtoTestFactory withAddress(AddressDtoTestFactory addressFactory) {
    return new UserDtoTestFactory(firstName, lastName, age, addressFactory);
  }
}
2

raczej skłaniałbym się ku opcji a. Dodatkowo na potrzeby testów możesz dodać jakiegoś builder. Jako przykład mogę pokazać coś takiego gdzie imo przez buildera tworzenie dość złożonej struktury na potrzeby testów jest w miarę czytelne

EDIT:
https://pastebin.com/2UiwQT7y

0

Tak, ja osobiście sam użyłem wersji a), natomiast przypadki, z którymi miałem doczynieniapóki co były na tyle pojedyńcze ( np. tworzyłem stałą z instancją i wybranymi parametrami bo tylko 2 testy jej używały), że jakoś boli mnie budowanie całej fabryki do tego, z drugiej strony konstruktory na tyle "potężne", że kłuje w oczy pierdylion nulli. Dochodzę teraz do wniosku odrobinę, że w niektórych przypadkach zrobienie metody ze zagregowanymi stubowanymi geterami usuwa problem wielu nulli oraz problem rozległego kodu

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