What is the idiomatic (and easiest) way of configuring integration tests in Spring?
For example let's define a sample integration test:
#ActiveProfiles("dev", "test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SampleTest() {
#LocalServerPort
lateinit var port: Number
lateinit var client: WebTestClient
#BeforeEach
fun setup() {
client = WebTestClient.bindToServer().baseUrl("http://localhost:$port").build()
}
#Test
fun test() {
client.get()
.uri("/api/something")
.exchange()
.expectStatus().is2xxSuccessful
.expectBody()
}
}
There are many ways i can configure WebTestClient but I don't know which one is "the best". To name a few:
lateinit var and BeforeEach
configure in each test separately
Given my project's structure and conventions most of things we get from DI is injected via constructor using #Autowired annotation. Is it also possible in this case? Therefore i'd just do:
#ActiveProfiles("dev", "test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SampleTest(
#Autowired val client: WebTestClient
) {
#Test
fun test() {
client.get()
.uri("/api/something")
.exchange()
.expectStatus().is2xxSuccessful
.expectBody()
}
}
I've tried to do it without other modifications and some of my tests fail because of connection refused
Related
After searching around the net for few days, couldn't really find the resources I need for my use case as many of it are implemented in Java.
I have a service class that is calling external api, and I am trying to write unit tests on it with MockWebServer
#Service
class ApiService {
#Autowired
private lateinit var webClient: WebClient
fun getApiResult(name: String): ResultDto? {
return try {
webClient
.get()
.uri("https://www.example.com/")
.retrieve()
.bodyToMono(Result::class)
.block()
catch {
null
}
}
}
#ExtendWith(MockitoExtension::class)
#MockitoSettings(strictname = Strictness.LENIENT)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ApiServiceTest {
#Mock
private lateinit var webClient: WebClient
#InjectMocks
private lateinit var mockApiService: ApiService
private val mockName = "mockName"
private val mockDetailDto = DetailDto(name = "mockName", age = 18)
#Test
fun canGetDetails() {
mockWebServer.enqueue(
MockResponse()
.setResponse(200)
.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody(mockDetailDto))
)
mockApiService.getApiResult(mockName)
val request: RecordedRequest = mockWebServer.takeRequest()
// Assert statements with request
}
}
However, I am getting lateinit property webClient has not been initialised. But if I were to use #Spy instead of #Mock for the webClient in my test, it will be making actual apis calls which is not the goal of unit test. My goal would be able to check the request for some of the details such as GET and its return value. Appreciate the community's help.
By my opinion you not add some configuration, for resolve it automatically you can try make Integration Test for this. Like this
#WithMockUser//if you use spring secutity
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = RANDOM_PORT)
class MyTest {
#Autowired
private MockMvc mvc;
}
and make a test application for quickly loading environment
#SpringBootApplication
#ConfigurationPropertiesScan
#ComponentScan(lazyInit = true)//make loading is faster
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
This way can load full context configuration like prod mode, of course only for called methods if you used lazyInit = true
I have a spring boot service that validates each request by calling my auth service. Now I am writing an integration test. How can I mock my request to auth service while testing my own APIs?
#GetMapping("/pending/task")
#Operation(summary = "Get user's pending task", tags = "UserTask", security = {#SecurityRequirement(name = Constants.AUTH_TOKEN_HEADER)})
#PreAuthorize(Constants.PreAuthorize.ROLE)
public List<UserTaskDto> getPendingTasks(#Valid #RequestParam long courseId){
// internal logic
}
SpringBoot filter will read the token from the header and verify that against auth service using rest. I want to mock that call during this api testing.
Test Code
class UserTaskControllerTest extends ApplicationTests {
#Mock
RestTemplate restTemplate;
#Test
void shouldGiveAllUserPendingTask(){
HttpHeaders headers = new HttpHeaders();
headers.add(Constants.AUTH_TOKEN_HEADER, GENERIC_AUTH_TOKEN);
Task task = FactoryClass.createTask();
UserTask userTask = FactoryClass.createUserTask();
CentralAuthInfo centralAuthInfo = FactoryClass.getCentralAuthInfo();
taskRepository.save(task);
userTask.setTask(task);
userTaskRepository.save(userTask);
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(USER_PENDING_TASK_BASE_URL)
.queryParam(COURSE_ID, userTask.getCohortId());
when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(HttpEntity.class), ArgumentMatchers.eq(CentralAuthInfo.class))).thenReturn(new ResponseEntity<>(centralAuthInfo, HttpStatus.OK));
ResponseEntity<UserTaskDto> responseEntity = testRestTemplate.exchange(builder.toUriString(), GET, new HttpEntity<>(headers), UserTaskDto.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(200);
}
Spring offers a #WithMockUser that you can add to your tests. I usually use it with a WebTextClient to test API calls. Here is an example with reactive controller but same applies to non-reactive
#Import(SecurityConfig.class)
#WebFluxTest(MyController.class)
class MyControllerTest {
#Autowired
private WebTestClient webTestClient;
...
#Test
#WithMockUser(username="admin",roles={"USER","ADMIN", "ROLE"})
void testPendingTasks() {
webTestClient
.get()
.uri("/pending/task")
.exchange()
.expectStatus()
.isOk();
}
...
}
Maybe #AutoConfigureMockMvc(addFilters = false) will help to disable security for integration test, like this:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc(addFilters = false)
public class FooTest {
#Autowired
private MockMvc mockMvc;
// some of your tests here ....
}
If it's not what you need you can just create a fake auth service class for test purposes and override the behavior in the way you want.
I have tried to use both MockMVC and TestRestTemplate. In both cases, the response back is 404 but the API endpoints work outside of integration test (when I run the spring app on its own).
Does anyone have a working sample app that has a working integration test for a generated controller using Spring Data Rest?
I was also able to write regular integration tests against my own controllers (Non SDR types)
Test code:
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyTest {
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void testApi() {
String settings = testRestTemplate
.getForObject("/api/v1/orders", String.class);
System.out.println(settings);
}
}
Repo:
#RepositoryRestResource(excerptProjection = OrderSummaryProjection.class)
public interface OrderRepository extends JpaRepository<Order, Long> {}
Ok I found out the issue but I dont know what the answer should be:
I set spring.data.rest.basePath in application.properties.
But I don't think that file is read when you run the integration tests. How do I fix that?
I currently don't test Spring Data Rest endpoints, but if I were to do it, I would test interfaces using classical Integration test approach:
#RunWith(SpringRunner.class)
#SpringBootTest
public class DummyIT {
#Autowired
private SettingsRepository settingsRepository;
#Test
public void testApi() {
List<Settings> settings = settingsRepository.findAll();
assertNotNull(settings);
}
}
I also tested end-to-end test and it also works, it just returns ugly {"_embedded" : {"settings" : [ { ... } ] }, ... } so it's doable, but it's not pretty:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DummyTest {
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void testApi() {
String settings = testRestTemplate
.getForObject("/api/settings", String.class);
System.out.println(settings);
}
}
I need to create an integration test against a REST API. My service is using Resttemplate as HTTP client. The client code is generated from swagger file.
Running the test yields an error java.lang.AssertionError: No further requests expected: HTTP GET
It seems that the test is running against a mock server. How to let the test run against the real server?
This is my current test setup (want to cut out a minimal test frame to get a fast test - booting the complete context is far too slow):
#RunWith(SpringRunner.class)
#Import(value = { TpzConfig.class, TpzServiceRestImpl.class, ManufacturingPlantPhPmMapperImpl.class,
ProductHierarchyMapperImpl.class, PlantMapperImpl.class })
#ActiveProfiles(profiles = { "tpz" })
#RestClientTest
public class TpzServiceRestImplTest {
#Autowired
private TpzService to;
#MockBean
private ProductionPlantService ppService;
#MockBean
private ProductHierarchyService phService;
#Test
public void test() {
List<ProductManufacturer> pmByProductHierarchy = to.pmByProductHierarchy("001100909100100388");
}
}
I need #RestClientTest to have a bean of RestTemplateBuilder.
Is there a way to configure #RestClientTest to use the real server (similar to #DataJpaTest where i can configure not to use h2)?
#RestTemplateTest give you pre-configured RestTemplateBuilder and MockRestServiceServer.
1.You could #Autowired MockRestServiceServer and mock expected HTTP calls.
2.Remove the auto configuration :
#RestClientTest(excludeAutoConfiguration = MockRestServiceServerAutoConfiguration.class)
But that make the test kind of slow.. There is maybe a way to optimize it.
3.In another hand, you could remove #RestClientTest and in a test configuration file, create a bean of RestTemplateBuilder. Something like this :
#TestConfiguration
public class TestConfig {
#Bean
public RestTemplateBuilder getRestTemplateBuilder() {
return new RestTemplateBuilder();
}
}
After this, add this configuration file in your imports :
#Import(value = { TpzConfig.class, TpzServiceRestImpl.class,
ManufacturingPlantPhPmMapperImpl.class, ProductHierarchyMapperImpl.class,
PlantMapperImpl.class, TestConfig.class })
And you should be good for your test.
We are in the process of creating from the scratch product with spring webflux. We are writing our unit test cases. Though I can able to get the Spring Application main class in my import, when we run mvn clean install, it is keep on telling that Compilation failure, cannot find class. How we can overcome this?
My project structure is,
Application
-app-web-module
-src/java/com/org/SpringApplicationClass
-pom.xml
-app-web-unitcases
-src/test/com/org/mytestclass
-pom.xml
And my test class is,
#TestPropertySource(properties = "CONFIG_ENVIRONMENT=ci")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureWebTestClient
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#ContextConfiguration(classes = com.org.MainApplication.class)
public class BaseACTest {
#Autowired
private WebTestClient webTestClient;
#BeforeAll
public void init1() {
MockitoAnnotations.initMocks(this);
}
#BeforeEach
public void init() {
Assertions.assertNotNull(webTestClient);
}
#Test
public void testGetAllAmenities() {
webTestClient.get().uri("/urltobeplaced/1234")
.header("X-Request-ID", "123")
.header("X-Session-ID", "123")
.header("X-Application-ID", "123")
.exchange()
.expectStatus().isOk();
}
}