@SpringBootTest etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
@SpringBootTest etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

29 Kasım 2020 Pazar

SpringBoot Test @SpringBootTest Anotasyonu properties Alanı

Giriş
Bazı bean'lerin testlerde yüklenmemesi için kullanılabilir

Örnek
@EnabledScheduling anotasyonlu sınıfların testlerde yüklenmemesini isteyebiliriz. Bu durumda @EnabledScheduling kullanan sınıfları yüklenmesini bir tane property değer ile kontrol etmek gerekir.

Kodumuzu şu hale getirelim. Böylece @EnableScheduling sadece io.reflectoring.scheduling.enabled property değeri true iken etkin olur
@Configuration
@EnableScheduling
@ConditionalOnProperty(
        name = "io.reflectoring.scheduling.enabled",
        havingValue = "true",
        matchIfMissing = true)
public class SchedulingConfiguration {
}
Test kodumuzda şöyle yapalım. SchedulingConfiguration sınıfının bean olarak yüklenmediği dolayısıyla da @EnabledScheduling anotasyonunun etkin olmadığı görülebilir.
@SpringBootTest(properties = "io.reflectoring.scheduling.enabled=false")
class SchedulingTest {

  @Autowired(required = false)
  private SchedulingConfiguration schedulingConfiguration;

  @Test
  void test() {
    assertThat(schedulingConfiguration).isNull();
  }
}
Örnek
Teste mahsus bir property değeri atamak için kullanılabilir. Şöyle yaparız
@SpringBootTest(properties = "foo=bar")
class SpringBootPropertiesTest {

  @Value("${foo}")
  String foo;

  @Test
  void test(){
    assertThat(foo).isEqualTo("bar");
  }
}

20 Ekim 2020 Salı

SpringBoot Test @SpringBootTest Anotasyonu classes Alanı

Giriş 
Bu alan en önemli ve kullanımı karışık alan bu yüzden ayrı bir yazıya taşımak istedim. Açıklaması şöyle
The component classes to use for loading an ApplicationContext. Can also be specified using @ContextConfiguration(classes=...). If no explicit classes are defined the test will look for nested @Configuration classes, before falling back to a @SpringBootConfiguration search.
1. Bu Alan Tanımlı Değilse

1.1 Bu Anotasyonla Beraber @ContextConfiguration Varsa
O zaman sanki bu alana değer tanımlanmış gibi @ContextConfiguration ile tanımlı bean'leri yükler.  
@ContextConfiguration SpringBoot gelmeden önceki standart spring test anotasyonudur. 

Örnek
Hem bu alana değer atamazsak ve tek bir bean ile ApplicationContext yaratmak istersek şöyle yaparız.
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "spring.profiles.active=test")
@ContextConfiguration(classes = MyTestBean.class)
1.2 Test Sınıfı İçinde @Configuration Anotasyonu Varsa
@Configuration ile tanımlı bean'leri yükler
Örnek
Tüm uygulama yerine sadece belli bean'leri ayağa kaldırmak isteyelim. Test içinde @Configuration olarak işaretli static sınıf yaratırız. Şöyle yaparız.
@RunWith(SpringRunner.class)
@SpringBootTest
public class SomeTest {

    @Configuration
    static class ContextConfiguration {
        @Bean
        @Primary //may omit this if this is the only SomeBean defined/visible
        public SomeBean someBean () {
            return new SomeBean();
        }
    }

    @Autowired
    private SomeBean someBean;

    @Test
    public void testMethod() {
        // test
    }
}
1.3 Test Sınıfı İçinde @TestConfiguration Anotasyonu Varsa
Örnek
Tüm uygulamadaki bean'lere ilave olarak bazı bean'leri daha ayağa kaldırmak isteyelim. Test içinde @TestConfiguration olarak işaretli bir static sınıf yaratırız.

1.4 Test Sınıfı İçinde @Configuration Anotasyonu Yoksa
@SpringBootConfiguration anotasyonlu sınıfı arar ve onu yükler. Bu sınıf ta tüm Spring uygulamasıdır
Örnek
@SpringBootApplication olarak işaretli sınıfın paket olarak test sınıfının paketinde olması gerekir. Eğer @SpringBootApplication olarak işaretli sınıf bulunamazsa şu hatayı alırız.
Unable to find a @SpringBootConfiguration, you need to use 
@ContextConfiguration or @SpringBootTest(classes=...) ...

2. Bu Alan Tanımlıysa

2.1 Sadece Belirtilen Sınıfları Yükler
Yüklenen sınıf yine @SpringBootApplication uygulaması olabilir

Örnek
Tüm uygulama yerine sadece belli bean'leri ayağa kaldırmak isteyelim. Elimizde şöyle bir kod olsun.
@Configuration
public class ApplicationConfig {

  @Bean
  public MapperFactory mapperFactory() {
    return new DefaultMapperFactory.Builder().build();
  }
}
Şöyle yaparız.
@SpringBootTest(classes = ApplicationConfig.class)
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
public class ProductSupplierIntegrationTests {
  // tests
}
Örnek
Test sınıfımızın paketi şöyle olsun com.example.test.JpaTest
@SpringBoot olarak işaretli sınıfı com.example paketinde arar. Eğer  sınıfımız com.example.Application ise sorun yoktur. Ancak sınıfımız com.example.application.Application ise hata alırız. Bu durumda en doğrusu @SpringBoot olarak işaretli sınıfı açıkça belirtmek. Şöyle yaparız
@SpringBootTest(classes = Application.class)
Örnek
Yine aynı şekilde farklı bir paketteki tüm uygulamayı ayağa kaldırmak isteyelim. Elimizde şöyle bir kod olsun.
@SpringBootApplication
public class Application {

  public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
  }
}
Şöyle yaparız.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ApplicationTest{

  @Autowire
  Foo foo //whatever you are testing

  @Test
  public void FooTest() throws Exception{
    Foo f = foo.getFooById("22");
    assertEquals("9B". f.getCode); 
  }
}

21 Ağustos 2019 Çarşamba

SpringBoot Test @SpringBootTest Anotasyonu

Giriş
Şu satırı dahil ederiz
import org.springframework.boot.test.context.SpringBootTest;
Açıklaması şöyle
spring-boot-test-starter (in the test scope) contains Junit5、Mockito、 Spring Testlibraries and we find these common libraries to be useful when writing tests.
Spring ile integration test yapmayı sağlayan anotasyonlardan birisidir. Bu anotasyon unit test için değildir, çünkü eğer özel bir ayar yapmadıysak tüm application context'i ayağa kaldırır. Yani tüm uygulama çalışır. Eğer uygulamamızdaki bazı katmanları test etmek istiyorsak Test Slices başlığı altında geçen, ve daha kısıtlı bir application context yaratılmasını sağlayan diğer anotasyonları kullanmak gerekir.

Eğer amacımız integration test değilse
Eğer amacımız integration test değilse :
- Tüm application context yerine @ContextConfiguration anotasyonu ile teste kullanılacak bean'leri yaratabiliriz.
- @TestConfiguration anotasyonu ile testte kullanılacak bean'leri yaratabiliriz
- Basit JUnit kullanılabilir.
- Mockito ile mock kullanabiliriz

Mockito Kullanma
Örnek - JUnit5
Şöyle yaparız
@ExtendWith(MockitoExtension.class) public class ProductManagerTest { @InjectMocks private ProductManager productManager; @Mock private ProductDAO productDAO; @Test public void givenProduct_whenCreate_thenReturnProduct() { } }
Örnek - JUnit4
Elimizde şöyle bir kod olsun
@Service
public class Demo{
  @Autowired
  private Demo1 demo1;
  public Boolean doInvoke() {
    //do something
  }
}

@SpringBootTest(classes=Demo.class)
public class demoTest{

  @Autowired
  private Demo demo;
  @MockBean
  private Demo1 demo1;
  @Test
  public void test() {
    BDDMockito.given(demo1.test).willReturn(true);
    Boolean result = demo.doInvoke();
    Assertions.assertTrue(result);
  }
}
Burada @SpringBootTest kullanmaya gerek yok. Şöyle yaparız
@RunWith(MockitoJUnitRunner.class) public class DemoTest{ @Mock private Demo1 demo1; @InjectMocks private Demo demo; @Test public void test() { BDDMockito.given(demo1.test).willReturn(true); Boolean result = demo.doInvoke(); Assertions.assertTrue(result); } }
Eğer amacımız integration test ise
- Customization için @SpringBootTest anotasyonunun properties alanı kullanılabilir.
- @ActiveProfiles anotasyonu kullanılabilir. Böylece belirtilen application-<profile>.properties veya application-<profile>.yml dosyaları kullanılır
- @TestPropertySource anotasyonu kullanılabilir.
- @MockBean anotasyonu ile gerçek bean yerine mock bir bean kullanabiliriz
- @TestConfiguration anotasyonu ile uygulamadaki bean'lere ilave olarak yeni bir test bean'i yaratabiliriz ,veya bir bean'i override edebiliriz.
- @Configuration anotasyon ile teste mahsus bir primary configuration yaratabiliriz.
@SpringBootTest anotasyonunun classes alanı kullanılarak application context içinde hangi sınıfların olacağı belirtilir
- Integration test için yine tüm context'i kaldırmaya gerek olmayabilir. Bunun için SpringBoot Test Sliced Test Anotasyonları yazısına da bakabilirsiniz

@SpringBootTest Ne Yapar
Test sınıfının bulunduğu paketten başlayarak yukarı doğru çıkar ve @SpringBootConfiguration anotasyonunu arar. Bu anotasyonu ayrı olarak tanımlamadıysak @SpringBootApplication yani main sınıfımız bu anotasyonu sağlar.

ApplicationContext  Cache'lenmesi
Açıklaması şöyle
Starting a Spring context for your test takes a good amount of time. Especially if you populate the whole context using @SpringBootTest. This adds an additional overhead (time-wise) to your test execution and may drastically increase your total build time.

Fortunately, Spring Test provides a mechanism to cache an already started application context and reuse it for subsequent tests. You can think of a basic Map that stores different application contexts. Spring Test creates a uniquely identifiable key for the Map entry.
Açıklaması şöyle.
Spring Boot provides a @SpringBootTest annotation which can be used as an alternative to the standard spring-test @ContextConfiguration annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests via SpringApplication. The Spring TestContext framework stores application contexts in a static cache. This means that the context is literally stored in a static variable. In other words, if tests execute in separate processes the static cache will be cleared between each test execution, and this will effectively disable the caching mechanism. To benefit from the caching mechanism, all tests must run within the same process or test suite. This can be achieved by executing all tests as a group within an IDE
Açıklaması şöyle.
By default, once loaded, the configured ApplicationContext is reused for each test. Thus the setup cost is incurred only once per test suite, and subsequent test execution is much faster. In this context, the term test suite means all tests run in the same JVM
ApplicationContext  Cache'lenmesini Engelleyen Şeyler
Eğer testte @Autowired varsa sorun yok
Ama testlerdeki bazı anotasyonlar cache'i kullanmayı engelliyor. Bu da testlerin yavaş çalışmasına sebep olur. Açıklaması şöyle. Bunlardan en problemli olanlar @MockBean ve @SpyBean
By reading these articles and the same ones, I realized that there are some pitfalls in integration tests that prevent the Spring from reusing the loaded application context in integration tests. Here are the most important ones: 

- Using @MockBean and @SpyBean
- Using @DirtiesContext
- Careless use of profiles in integration tests
- Careless use of @TestPropertySource
MockMvc Inject Edilmesi
Eğer gerçek bir web sunucusu kullanmıyorsak, tüm application context'i ayağa kaldırıdıp MockMvc kullanmak mümkün. Burada @AutoConfigureMockMvc ile bir adet MockMvc autowire ediliyor.
Örnek
Şöyle yaparız
@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
class RegisterUseCaseIntegrationTest {

  @Autowired
  private MockMvc mockMvc;

  @Autowired
  private ObjectMapper objectMapper;

  @Autowired
  private UserRepository userRepository;

  @Test
  void registrationWorksThroughAllLayers() throws Exception {
    UserResource user = new UserResource("Zaphod", "zaphod@galaxy.net");

    mockMvc.perform(post("/forums/{forumId}/register", 42L)
            .contentType("application/json")
            .param("sendWelcomeMail", "true")
            .content(objectMapper.writeValueAsString(user)))
            .andExpect(status().isOk());

    UserEntity userEntity = userRepository.findByName("Zaphod");
    assertThat(userEntity.getEmail()).isEqualTo("zaphod@galaxy.net");
  }

}
TestRestTemplate Inject Edilmesi
TestRestTemplate yazısına taşıdım

classes Alanı
classes Alanı yazısına taşıdım

webEnvironment Alanı
Açıklaması şöyle. Eğer bu alanı tanımlarsak bir tane web sunucusu yaratılır.
By default, @SpringBootTest will not start a server. You can use the webEnvironment attribute of @SpringBootTest to further refine how your tests run:
Sunucuyu değişik modlarda başlatmak mümkün. Açıklaması şöyle.
This annotation configures a complete test environment with all your beans and everything set up. You can choose to start a mock servlet environment, or even start a real one with the webEnvironment attribute of the @SpringBootTest annotation.
Şu değerleri alabilir.
MOCK
RANDOM_PORT
DEFINED_PORT
NONE

DEFINED_PORT Değeri
Gerçek bir servlet ortamı başlatır.

RANDOM_PORT Değeri
Açıklaması şöyle. Gerçek bir servlet ortamı başlatır.
Loads a WebServerApplicationContext and provides a real web environment. Embedded servers are started and listen on a random port.
Örnek
Şöyle yaparız
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)