How to connect to local running server in springboot testing - spring

My application is running in local with port 8089, I am trying to do Spring Rest Controller testing.
It is running in 8089 port.
In application.yml
spring:
profiles:
active: sit
In application-sit.yml
server:
port: 8089
servlet:
context-path: /myapp
In My base test case:
import org.junit.Test;
import org.junit.runner.RunWith;
#RunWith(SpringJUnit4ClassRunner.class)
//#SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
//#WebAppConfiguration
#SpringBootTest(classes = MyApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#ActiveProfiles("sit")
public abstract class FullOtpControllerTest {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
#Value("${server.port}")
int serverPort;
#Test
public void sendOtpTest() throws Exception {
String uri = "/sendOtp";
//{"clientId": "default2","tag": "tag","mobileNumber": "9999988888"}
SendOTPRequestDTO otpRequest = new SendOTPRequestDTO();
otpRequest.setClientId("default2");
otpRequest.setTag("tag");
otpRequest.setMobileNumber("9321901416");
String inputJson = mapToJson(otpRequest);
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.post(uri)
//.accept(MediaType.APPLICATION_JSON_VALUE)).andReturn()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(inputJson)).andReturn();
int status = mvcResult.getResponse().getStatus();
String content = mvcResult.getResponse().getContentAsString();
assertEquals(8089,serverPort);
assertEquals(200, status);
}
}
But , first assert is passed. but in the debug it showing different port as showing in the below image.
I run: OtpControllerTest.sendOtpTest and I am getting:
java.lang.AssertionError:
Expected :200
Actual :404

MockMVC is intended for a different kind of testing.
It works in conjunction with #WebMvcTest annotation and allows testing your controller definitions: annotations, parameters, etc.
In this case spring starts a "splice" of the application that is relevant to the WEB layer, for example, it won't load beans annotated with #Service or #Repository, but will load all the #RestController annotated classes. If the controller depends on some service, you can mock it with a #MockBean annotation
In any case it doesn't require any kind of remote application everything runs "in-place".
#SpringBootTest annotation serves a different purpose, so you shouldn't run tests annotated with this annotation and use MockMVC instead:
#SpringBootTest runs the whole microservice (loads all the beans). Alternatively, there is a "mode" where spring boot test loads only explicitly defined Context Configurations...
If your purpose is black-box testing of the APIs of a remote application - you don't have to use spring at all - you don't need beans, don't need injection - you only need an Http Client of some sort (doesn't even have to be in Java) to implement this kind of tests.
One framework (out of many) is Rest Assured but again there are many choices here.

Related

PostConstruct and test

I use spring boot 3
Main spring boot class
#EnableTransactionManagement
#SpringBootApplication
#Slf4j
public class FlexApplication{
private final ApplicationParameterManager appParamManager;
public FlexApplication(ApplicationParameterManager appParamManager) {
this.appParamManager = appParamManager;
}
#PostConstruct
public void init(){
}
....
}
#Service
#Slf4j
public class ApplicationParameterManager{
....
}
Basic test
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class ListUserRepositoryTest {
#Autowired
private ListUserRepository repository;
#Test
public void getListUserByUserType(){
String typeUser = "CETEST";
Pageable page = Pageable.ofSize(10);
Page<ListUser> pageListUser = repository.findAllByTypeUser(typeUser, page);
assertThat(pageListUser.getContent().size() > 5 ).isTrue();
}
}
Otherwise this test, application run well
I get this error
Parameter 0 of constructor in com.acme.FlexApplication required a bean
of type 'com.acme.parameter.ApplicationParameterManager' that could
not be found.
I think it is not related to version of Spring Boot.
As you're using #DataJpaTest , your bean is not created
Spring Docs:
#DataJpaTest can be used if you want to test JPA applications. By
default it will configure an in-memory embedded database, scan for
#Entity classes and configure Spring Data JPA repositories. Regular
#Component beans will not be loaded into the ApplicationContext.
Solution would be to use #SpringBootTest instead of #DataJpaTest if your test is not really a JPA test.
Also, still using #DataJpaTest you could add #Import(ApplicationParameterManager.class) to your test class
When using #DataJpaTest, you are not creating the whole spring context as when you run the application normally but you only create the beans responsible for data access layer.
In order to run your tests properly, you need to provide a mocked bean of type ApplicationParameterManager.
The easiest way to do it is by utilizing #MockBean annotation.
So, to make your tests work, edit the test in the following way.
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class ListUserRepositoryTest {
#MockBean
private ApplicationParameterManager applicationParameterManager;
#Autowired
private ListUserRepository repository;
#Test
public void getListUserByUserType(){
String typeUser = "CETEST";
Pageable page = Pageable.ofSize(10);
Page<ListUser> pageListUser = repository.findAllByTypeUser(typeUser, page);
assertThat(pageListUser.getContent().size() > 5 ).isTrue();
}
}
That way, the spring context will include a mocked bean of your required dependency.
Take a look at #MockBean java doc for more information. https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html
If you prefer to run the whole spring context in order to perform full integration tests, take a look at #SpringBootTest annotation.
#DataJpaTest should be used when you want to test data access layer in isolation.

Spring Boot #RestClientTest how test with real server (not mock)

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.

Use Mockito to register spring bean when doing mockMvc testing

Here is my idea, I try to test my Restful controller with MockMvc
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc.perform(post(...).param(..))
.andExpect(...);
The API I call fires a rabbitmq message. But I don't want to test Amqp in the test. Instead, I create a mock producer like this:
#Mock
private AmqpProducer producer
I want to inject this producer into the spring context, so I can capture the method call producer.sendMessage and test the message.
To mock a bean in the Spring Boot contexte you cannot use directly #Mock.
It will create a mock for AmqpProducer but not which used by your container.
With Spring, the classical way to do that is annotating your test class with a specific context configuration class or file (#ContextConfiguration(...)) that provides the mock.
With Spring Boot, it is simpler : annotating your class with #WebMvcTest
and your field to mock with #MockBean is enough to mock the bean in the container (Spring guide).
Note that #WebMvcTest with a specified controller class specified in the annotation value will instantiate the specified controller and also all its direct dependencies declared. So you should mock all of them and not only which one that interests you in your unit test.
So it should look like :
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class WebMockTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private AmqpProducer producer;
#Test
public void foo() throws Exception {
this.mockMvc.perform(...);
verify(producer).sendMessage(expected);
}
}

Does #WebMvcTest require #SpringBootApplication annotation?

My goal is to migrate a Spring Boot application previously developed with Spring Boot 1.3 to the newest Spring Boot version 1.4. The application consists of several maven modules and only one of them contains class annotated with #SpringBootApplication.
One part of migration is to use #WebMvcTest annotation to efficiently test controllers, and here I get an issue.
Consider an example application from Spring Boot github page. #WebMvcTest annotation works perfectly, because, as far as I understand (after I did several tests), there is a class in the main package annotated with #SpringBootApplication. Note that I follow the same concept as shown in the example above for my own #WebMvcTest tests.
The only difference I see that in my application, controller classes are located in a separate maven module (without #SpringBootApplication annotated class), but with #Configuration and SpringBootConfiguration configurations. If I do not annotate any class with #SpringBootApplication I always get an assertion while testing controller. My assertion is the same as when SampleTestApplication class in the example above modified to have only #EnableAutoConfiguration and #SpringBootConfiguration annotations (#SpringBootApplication is not present):
getVehicleWhenRequestingTextShouldReturnMakeAndModel(sample.test.web.UserVehicleControllerTests) Time elapsed: 0.013 sec <<< FAILURE!
java.lang.AssertionError: Status expected:<200> but was:<404>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:54)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:81)
at org.springframework.test.web.servlet.result.StatusResultMatchers$10.match(StatusResultMatchers.java:664)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
at sample.test.web.UserVehicleControllerTests.getVehicleWhenRequestingTextShouldReturnMakeAndModel(UserVehicleControllerTests.java:68)
How should I deal with that? Should I always have class annotated with #SpringBootApplication in order to run #WebMvcTest tests?
EDIT 1: I did a small maven project with 2 modules and a minimal configuration. It is here. Now, I get NoSuchBeanDefinitionException exception for repository defined in another module. If I configure "full" #SpringBootApplication - everything is fine.
EDIT 2: I modified small test project from EDIT 1 to give an original issue. I was playing with different annotations and added #ComponentScan on configuration class, because I suspected that beans are not registered properly. However, I expect that only #Controller bean (defined in #WebMvcTest(...class)) shall be registered based on magic behind #WebMvcTest behaviour.
EDIT 3: Spring Boot project issue.
Short answer: I believe so.
Long answer:
I believe #WebMvcTest needs to find the SpringBootApplication configuration since WebMvcTest's sole purpose is to help simplify tests (SpringBootApplication would rather try to load the whole world).
In your specific case, since you don't have any in your non-test packages, I believe it also finds SampleTestConfiguration which is annotated with #ScanPackages and somehow loads every beans.
Add the following in src/main/java/sample/test
#SpringBootApplication
public class SampleTestConfiguration {
}
And change your test to this:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private MyService ms;
#Autowired
private ApplicationContext context;
#Test
public void getDataAndExpectOkStatus() throws Exception {
given(ms.execute("1")).willReturn(false);
mvc.perform(get("/1/data").accept(MediaType.APPLICATION_JSON_VALUE)).andExpect(status().isOk()).andExpect(content().string("false"));
}
#Test
public void testMyControllerInAppCtx() {
assertThat(context.getBean(MyController.class), is(not(nullValue())));
}
#Test
public void testNoMyAnotherControllerInAppCtx() {
try {
context.getBean(MyAnotherController.class);
fail("Bean exists");
} catch (BeansException e) {
// ok
}
}
}
#WebMvcTest finds the SpringBootApplication, then load only a limited number of beans (see documentation):
#WebMvcTest will auto-configure the Spring MVC infrastructure and
limit scanned beans to #Controller, #ControllerAdvice, #JsonComponent,
Filter, WebMvcConfigurer and HandlerMethodArgumentResolver. Regular
#Component beans will not be scanned when using this annotation.
WebMvcTest requires SpringBootApplication: WebMvcTest inherits many AutoConfiguration, so it needs SpringBoot to load them. Then it disables many other AutoConfiguration and your Controllers become easily testable.
The whole point of using WebMvcTest is when you have a SpringBootApplication and you wish to make it simpler to test by disabling all beans except Controllers. If you don't have SpringBootApplication, then why use WebMvcTest at all?
It's an old topic, but there is a solution which wasn't mentioned here.
You can create a class annotated with SpringBootApplication just in your test sources. Then, you still have a nice, multi-module structure of your project, with just one "real" SpringBootApplication.
Yes,according to the spring boot docs
The search algorithm works up from the package that contains the test until it finds a #SpringBootApplication or #SpringBootConfiguration annotated class. As long as you’ve structure your code in a sensible way your main configuration is usually found.
But after I started using #WebMvcTest,spring boot still try to load other beans, finally TypeExcludeFilter did the trick.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = {JzYsController.class} )
public class JzYsControllerTest {
private static final String REST_V4_JZYS = "/rest/v4/JzYs";
#Autowired
private MockMvc mockMvc;
#MockBean
private JzYsService service;
#Test
public void deleteYsByMlbh() throws Exception {
Mockito.when(service.deleteYsByMlbh(Mockito.anyString())).thenReturn(Optional.of(1));
mockMvc.perform(delete(REST_V4_JZYS + "?mbbh=861FA4B0E40F5C7FECAF09C150BF3B01"))
.andExpect(status().isNoContent());
}
#SpringBootConfiguration
#ComponentScan(excludeFilters = #Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
public static class config{
}
}
There is one more solution. You can not use #WebMvcTest, but configure MockMvc yourself through the builder
class TestControllerTest {
private MockMvc mvc;
#BeforeEach
public void setup() {
mvc = MockMvcBuilders.standaloneSetup(new TestController())
.build();
}
#Test
void test() throws Exception {
// When
var res = mvc.perform(MockMvcRequestBuilders.get("/test/test"));
// Then
res.andExpect(status().isOk());
}
}
But this solution may entail a number of other problems, such as problems with configurations, environment property injections, etc.

spring-boot #WebIntegrationTest is secured by default

I'm relatively new to spring-boot and now I would like to write some integration tests for my first application.
My application provides several REST webservices, the main application starts like this
#EnableAutoConfiguration
#RestController
#SpringBootApplication
public class DistributorWebserviceApplication implements
ApplicationContextAware {..}
The application itself seems to be properly configured but in the integration test the webservice endpoints are secured and one needs username and password!? I didn't configure this on purpose.
Integration test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = DistributorWebserviceApplicationIntegrationTestConfiguration.class)
#WebIntegrationTest("server.port=0")
public class DistributorWebserviceApplicationIntegrationTest {..}
Spring configuration
#EnableAutoConfiguration
#Configuration
public class DistributorWebserviceApplicationIntegrationTestConfiguration {..}
Calling a webservice in a test method like
String url = String.format("http://localhost:%s%s", serverPort,
DistributorWebserviceApplication.URL_QUEUE_DISTRIBUTOR_JOB);
String result = restTemplate.postForObject(url, distributorJob,
String.class);
produces the result
{"timestamp":1431006031152,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/queueDistributorJob"}
Is there any possibility to disable the default security configuration?
Thank you in advance!

Resources