JUnit ile Servis Katmanı Testi-2
Bu yazıda bir önceki yazıda bahsettiğimiz konuları daha ileri seviyeye taşımaya çalışacağız.
Bu yazıyla beraber ihtiyacımız olan çoğu konuyu ele almış olacağız.
Dolayısıyla özellikle yeni başlayan arkadaşların önceki yazıları da okumasını tavsiye ediyorum.
Servis Katmanı
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
package emlakcepte.service; import java.time.LocalDateTime; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; //imports @Service public class RealtyService { private static final int MAX_INDIVICUAL_REALTY_SIZE = 5; @Autowired private UserService userService; @Autowired private RealtyRepository realtyRepository; @Autowired private BannerServiceClient bannerServiceClient; private Logger logger = Logger.getLogger(RealtyService.class.getName()); public Realty create(RealtyRequest realtyRequest) { User foundUser = userService.getById(realtyRequest.getUserId()) .orElseThrow(() -> new UserNotFoundException("kullanıcı bulunamadı")); if (UserType.INDIVIDUAL.equals(foundUser.getType())) { validateIndividualRealtySize(foundUser); } Realty realty = convert(realtyRequest, foundUser); Banner bannerRequest = new Banner(String.valueOf(realty.getNo()), 1, "123123", "banner açıklaması"); BannerResponse bannerResponse = bannerServiceClient.create(bannerRequest); if (!HttpStatus.CREATED.equals(bannerResponse.getHttpStatus())) { logger.log(Level.WARNING, "Banner kaydedilemedi!"); throw new RuntimeException("Banner kaydedilemedi!"); } return realtyRepository.save(realty); } private void validateIndividualRealtySize(User foundUser) { List<Realty> realtyList = realtyRepository.findAllByUserId(foundUser.getId()); if (MAX_INDIVICUAL_REALTY_SIZE < realtyList.size()) { logger.log(Level.INFO, "Bireysel kullanıcı en fazla 5 ilan girebilir. userID : {0}", foundUser.getId()); throw new EmlakCepteException("indivual.user.realty.max.size"); } } private Realty convert(RealtyRequest realtyRequest, User foundUser) { Realty realty = new Realty(); realty.setNo(realtyRequest.getNo()); realty.setCreateDate(LocalDateTime.now()); realty.setStatus(RealtyType.PASSIVE); realty.setTitle(realtyRequest.getTitle()); realty.setProvince(realtyRequest.getProvince()); realty.setUser(foundUser); return realty; } } |
Servisin içerisinde farklı kurallar eklemeye çalıştım ki biraz test yazmamız farklılaşsın ve gündelik test ihtiyaçlarımızı bu yazıda daha iyi örneklendirebilelim.
Unit Test
İlk testimiz kodda gördüğümüz üzere bir kullanıcı bulamadığında beklediğimiz hatanın fırlatılması.
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 |
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.Mockito.times; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; 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.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) class RealtyServiceTest { @InjectMocks private RealtyService realtyService; @Mock private UserService userService; @Mock private RealtyRepository realtyRepository; @Mock private BannerServiceClient bannerServiceClient; @Test void it_should_throw_user_found_exception_when_user_is_null() { // given // when Throwable exception = catchThrowable(() -> realtyService.create(new RealtyRequest())); // then assertThat(exception).isInstanceOf(UserNotFoundException.class); } } |
İkinci testimiz ise bireysel kullanıcının reality(gayrimenkul) yayınlama hakkının dolması durumu.
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 |
Test void it_should_throw_emlakcepte_exception_when_indivual_user_has_max_realty_size() { // given Optional<User> user = getUser(1, UserType.INDIVIDUAL); Mockito.when(userService.getById(1)).thenReturn(user); Mockito.when(realtyRepository.findAllByUserId(1)).thenReturn(getRealtyList(6)); // when Throwable exception = catchThrowable(() -> realtyService.create(getRealtyRequest(1))); // then assertThat(exception).isInstanceOf(EmlakCepteException.class); assertThat(exception.getMessage()).isEqualTo("Bireysel kullanıcı en fazla 5 ilan girebilir"); verify(userService, times(1)).getById(Mockito.eq(1)); //direkt olarak 1 de diyebilirdik. örnek amaçlı ekledim verifyNoMoreInteractions(userService); verify(realtyRepository, times(1)).findAllByUserId(Mockito.anyInt()); Mockito.verify(realtyRepository, times(0)).save(Mockito.any(Realty.class)); verifyNoInteractions(bannerServiceClient); } private Optional<User> getUser(int id, UserType type) { } private List<Realty> getRealtyList() { return List.of(getRealty(1), getRealty(2), getRealty(3), getRealty(4), getRealty(5)); } private Realty getRealty(int id) { return new Realty(id, "test ilan", LocalDateTime.now(), RealtyType.ACTIVE); } |
Üstteki test methodunu incelediğimizde tam yedi tane assertion mevcut. Çünkü test yazdığımızda yapılmasını ve ya yapılmamasını istediğimiz durumlardan emin olmalıyız.
Servis methodundan daha sonra yapılacak bir geliştirme algoritmada açıklar oluşturabilir ve testi çalıştırdığımızda bir sorun görmeyebiliriz fakat canlı ortamdan hatalar almamız kuvvetle muhtemeldir.
Aşağıdaki verify methoduna baktığımızda isteğimiz sadece bir kez istediğimiz methodun herhangi bir integer değer ile çalışması.
Tabii herhangi bir değer değil de kendimiz de o method çalıştığında aldığı değeri verebilirdik fakat ben farklı kullanımları göstermek adına bu şekilde bıraktım.
1 2 3 4 5 |
verify(realtyRepository, times(1)).findAllByUserId(Mockito.anyInt()); |
Bir de aşağıdaki iki kullanımın önemli olduğunu düşünüyorum. Nedeni ise şöyle bahsetmeye çalıştığım gibi eğer userService tekrar kullanılmayacaksa bunu garanti altına almam gerekir ki daha sonra bu method refactor edildiğinde yanlışlıkla userService tekrar kullanılmasın.
1 2 3 4 5 6 7 |
verifyNoMoreInteractions(userService); verifyNoInteractions(bannerServiceClient); |
Not: Methodun refactor edilmesinden bahsettim. Buradadan da anlaşılacağı üzere bir methodu refactor edecekseniz öncelikle test yazmanız sizin için en güvenli yol olacaktır. Çünkü diğer türlü farklı kodlarla aynı işi yaptığınızı kanıtlayamazsınız.
Üçüncü testimize bakalım.
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 |
@Test void it_should_throw_runtime_exception_when_banner_response_status_is_not_created_for_indivual_user() { // given Optional<User> user = getUser(1, UserType.INDIVIDUAL); Mockito.when(userService.getById(1)).thenReturn(user); Mockito.when(realtyRepository.findAllByUserId(1)).thenReturn(getRealtyList(1)); Mockito.when(bannerServiceClient.create(Mockito.any(Banner.class))) .thenReturn(getBannerResponse(HttpStatus.BAD_REQUEST)); // when Throwable exception = catchThrowable(() -> realtyService.create(getRealtyRequest(1))); // then assertThat(exception).isInstanceOf(RuntimeException.class); assertThat(exception.getMessage()).isEqualTo("Banner kaydedilemedi!"); verify(userService, times(1)).getById(1); verifyNoMoreInteractions(userService); verify(realtyRepository, times(1)).findAllByUserId(1); Mockito.verify(realtyRepository, times(0)).save(Mockito.any(Realty.class)); verify(bannerServiceClient, times(1)).create(Mockito.any(Banner.class)); } |
Dördüncü testimiz ise beklediğimiz başarılı durumların oluştuğunda
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 |
@Test void it_should_save_realty() { // given Optional<User> user = getUser(1, UserType.INDIVIDUAL); Mockito.when(userService.getById(1)).thenReturn(user); Mockito.when(realtyRepository.findAllByUserId(1)).thenReturn(getRealtyList(4)); Mockito.when(bannerServiceClient.create(Mockito.any(Banner.class))) .thenReturn(getBannerResponse(HttpStatus.CREATED)); Realty realty = getRealty(1); Mockito.when(realtyRepository.save(Mockito.any())).thenReturn(realty); // when Realty responseRealty = realtyService.create(getRealtyRequest(1)); // then verify(userService, times(1)).getById(1); verifyNoMoreInteractions(userService); verify(realtyRepository, times(1)).findAllByUserId(1); Mockito.verify(realtyRepository, times(1)).save(Mockito.any(Realty.class)); verifyNoMoreInteractions(realtyRepository); verify(bannerServiceClient, times(1)).create(Mockito.any(Banner.class)); verifyNoMoreInteractions(bannerServiceClient); assertThat(responseRealty).isNotNull(); assertThat(responseRealty.getId()).isEqualTo(realty.getId()); assertThat(responseRealty.getTitle()).isEqualTo(realty.getTitle()); assertThat(responseRealty.getProvince()).isEqualTo(realty.getProvince()); assertThat(responseRealty.getStatus()).isEqualTo(realty.getStatus()); } |
Görüldüğü üzere bir method için dört tane test yazdık ve daha fazla yazabiliriz.
Bütün kodlara aşağıdaki linkten ulaşabilirsiniz.
https://gist.github.com/cemdrman/1ee116b3a5eaaf1ab6cd5a46091e3e59
Unit test yazılarına devam ediyor olacağız, yeni yazılarda görüşmek üzere.
Faydalı olması dileğiyle.