Instancio ile Unit Test’ler için Sahte Veri Oluşturma
Unit test yazarken mock(sahte) datalar ile çalışıyoruz. Çünkü amacımız belli bir methodun görevini istediğimiz gibi yaptığını doğrulama istiyoruz.
Sahte veriler üretmek zaman zaman zahmetli bir işlem olabiliyor. Instancio tam olarak bu problemi çözüyor. Yani bizim yerimize verdiğimiz modele göre sahte datalar üretiyor.
Instancio
Instancio verileri üretirken tabii ki veri tiplerinden yararlanıyor. Bunun yanı sıra bu kütüphane ile amacımız sadece sahte data üretmek değil, verinin içeriği de önemli.
Zaten unit test yazarken çoğu durumda, amacımız yazacağımız test methoduna uygun datanın oluşması. Aksi takdirde istediğimiz test senaryonu gerçekleştiremeyiz.
Aşağıdaki bazı komutlar ile temel anlamda kullanımları görebiliriz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Person person = Instancio.create(Person.class); Person personWithoutAgeAndAddress = Instancio.of(personModel) .ignore(field(Person::getAddress)) .create(); List<Person> list = Instancio.createList(Person.class); List<Person> list = Instancio.ofList(Person.class).size(10).create(); Person person = Instancio.createBlank(Person.class); // Output: // Person[name=null, address=Address[street=null, city=null, country=null]] Person person = Instancio.ofBlank(Person.class) .set(field(Address::getCountry), "Canada") .create(); // Output: // Person[name=null, address=Address[street=null, city=null, country=Canada]] |
JUnit 5 kullanıyorsanıza aşağıdaki dependency yeterli olacaktır.
1 2 3 4 5 6 7 8 9 10 |
<dependency> <groupId>org.instancio</groupId> <artifactId>instancio-junit</artifactId> <version>4.7.0</version> <scope>test</scope> </dependency> |
JUnit 4 için ise aşağıdaki yeterlidir.
1 2 3 4 5 6 7 8 9 10 |
<dependency> <groupId>org.instancio</groupId> <artifactId>instancio-core</artifactId> <version>4.7.0</version> <scope>test</scope> </dependency> |
Aşağıdaki controller için test yazmayı deneyelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@AllArgsConstructor @RestController @RequestMapping("/api/v1/customers") public class CustomerController { private final CustomerService customerService; @PostMapping public void save(@RequestBody CustomerSaveRequest request) { customerService.save(request); } @GetMapping public List<Customer> getAll() { return customerService.getCustomerList(); } } |
Unit Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
import com.fasterxml.jackson.databind.ObjectMapper; import com.patika.kitapyurdumcustomerservice.dto.request.CustomerSaveRequest; import com.patika.kitapyurdumcustomerservice.service.CustomerService; import jakarta.ws.rs.core.MediaType; import org.instancio.Instancio; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(CustomerController.class) class CustomerControllerTest { @Autowired private MockMvc mockMvc; @MockBean private CustomerService customerService; @Test void save() throws Exception { ObjectMapper mapper = new ObjectMapper(); String body = mapper.writeValueAsString(Instancio.create(CustomerSaveRequest.class)); mockMvc.perform(post("/api/v1/customers") .content(body) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); verify(customerService, times(1)).save(Mockito.any(CustomerSaveRequest.class)); } @Test void getAll() throws Exception { mockMvc.perform(get("/api/v1/customers") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); verify(customerService, times(1)).getCustomerList(); } } |
Aşağıdaki service class’ı için de test yazmayı deneyelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@Service @RequiredArgsConstructor @Slf4j public class CustomerService { private final CustomerRepository customerRepository; public void save(CustomerSaveRequest request) { Optional<Customer> foundCustomer = customerRepository.findByEmail(request.getEmail()); if (foundCustomer.isPresent()) { log.error(ExceptionMessages.EMAIL_ALREADY_EXIST); throw new KitapYurdumException(ExceptionMessages.EMAIL_ALREADY_EXIST); } Customer customer = CustomerConverter.toCustomer(request); customerRepository.save(customer); log.info("customer created. {}", customer.getEmail()); } } |
Unit Test
Bir çok durumda test verisi oluşturmak uğraştırıcı ve sıkıcı olabiliyor.Eğer özel bir veriye ihtiyacımız varsa Instancio bunun bir için de çözüm sunuyor.
Set
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
package com.patika.kitapyurdumcustomerservice.service; import com.patika.kitapyurdumcustomerservice.dto.request.CustomerSaveRequest; import com.patika.kitapyurdumcustomerservice.exception.ExceptionMessages; import com.patika.kitapyurdumcustomerservice.exception.KitapYurdumException; import com.patika.kitapyurdumcustomerservice.model.AccountType; import com.patika.kitapyurdumcustomerservice.model.Customer; import com.patika.kitapyurdumcustomerservice.repository.CustomerRepository; import org.instancio.Instancio; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.instancio.Select.field; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class CustomerServiceTest { @InjectMocks private CustomerService customerService; @Mock private CustomerRepository customerRepository; @Test void shouldThrowException_whenUserAlreadyExist() { //given CustomerSaveRequest request = Instancio.of(CustomerSaveRequest.class) .set(field(CustomerSaveRequest::getEmail), email) .set(field(CustomerSaveRequest::getName), "bilisimio") .create(); Mockito.when(customerRepository.findByEmail(email)) .thenReturn(Optional.of(Instancio.of(Customer.class) .set(field(Customer::getEmail), email) .set(field(Customer::getIsActive), false) .set(field(Customer::getAccountType), AccountType.PLATINUM) .create())); //when KitapYurdumException exception = assertThrows(KitapYurdumException.class, () -> customerService.save(request)); //then assertThat(exception).hasMessage(ExceptionMessages.EMAIL_ALREADY_EXIST); verifyNoMoreInteractions(customerRepository); } } |
Supply
supply methodu ile verdiğimiz veri tipine göre istediğimiz veriyi atayabiliriz.
1 2 3 4 5 6 7 8 9 |
CustomerSaveRequest request = Instancio.of(CustomerSaveRequest.class) .set(field(CustomerSaveRequest::getEmail), email) .set(field(CustomerSaveRequest::getName), "bilisimio") .supply(all(LocalDate.class), () -> LocalDate.now()) // <strong>bütün</strong> <strong>LocalDate'lere</strong> istediğimiz değeri atayabiliriz .create(); |
withNullable
Yine senaryo gereği bazı değişkenlerimiz null olabiliyor ve bu durum için de withNullable methodu kullanabiliriz.
1 2 3 4 5 6 7 8 9 10 11 |
CustomerSaveRequest request = Instancio.of(CustomerSaveRequest.class) .set(field(CustomerSaveRequest::getEmail), email) .set(field(CustomerSaveRequest::getName), "bilisimio") .supply(all(LocalDate.class), () -> LocalDate.now()) // bütün LocalDate'lere istediğimiz değeri veriyoruz .withNullable(field(CustomerSaveRequest::getSurname)) .withNullable(field(CustomerSaveRequest::getProvince)) .create(); |
Genel olarak kendi kullandığım methodları örneklemeye çalıştım. Daha fazlası için kendi sayfasına bakabilirsiniz.
Faydalı olması dileğiyle.