I was developing the RESTful web service with springmvc4 and spring data jpa.Well, I have about 100+ apis for frontend to pull data.What I am want to do is how to test all of my apis automatically with random data.
The apis look like:
#RestController
#Api(tags = "index")
#RequestMapping("/index")
public class IndexController {
#Autowired
private IndexService indexService;
#RequestMapping(value = "/data", method = RequestMethod.GET)
#ApiOperation(value="today's data",notes="today's data",consumes="application/json",produces="application/json")
public Object getTodayData() {
return indexService.getTodayData();
}
#RequestMapping(value = "/chartData", method = RequestMethod.GET)
#ApiOperation(value="charts data",notes="charts data",consumes="application/json",produces="application/json")
public Object getLast7Data() {
return indexService.getLast7Data();
}
}
if I test it with postman one by one,it was waste a lot of time.When we developping,we should make sure the service is ok by ourselves.
I have got a solution but which is not satisfied me well.
here are my solution:
Scaned the controller of the specified package,then use reflection
get the annotation of the class,which could get the value of
#RequestMapping("/index").
Iterate through the method of the class and get the method's
annotation the same way,and get the full url.
Create random data for request, execute request and log the response.
Could anyone provide a solution for this, very appreciate for your help.
I see that you are using swagger in your api, you can use it to generate client code https://github.com/swagger-api/swagger-codegen for automatic testing.
Since you are using the Spring framework, you can try the following :
Use Spring Integration Test for testing the API. It spawns an
instance of your service and tests against it.
Use RestAssured & JUnit to hit the API and assert the response.
Use RequestMappingHandlerMapping.getHandlerMethods(), which you can simply get with Spring injection, e.g. via #Autowired. This will give you a map RequestMappingInfo->HandlerMethod, which contains all the information you need.
You can run the tests as regular JUnit tests, without the need for postman etc. using Spring integration testing support:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextHierarchy({
#ContextConfiguration(name = "root", locations = "classpath:applicationContext.xml"),
#ContextConfiguration(name = "web", locations = "classpath:xxx-servlet.xml)
})
public class YourTest extends AbstractTransactionalJUnit4SpringContextTests {...}
In this test, use #Autowired WebApplicationContext and pass it to MockMvcBuilders.webAppContextSetup(webApplicationContext) to create a MockMvc instance. It allows to submit HTTP request to the Spring's MockMvc infrastructure via an easy interface.
Note that Spring's MockMvc framework will not run any real app server such as Tomcat. But this might be exactly what you need, since it is much faster. By default, Spring integration testing framework will only initialize your Spring application context once for all the tests with the same Spring configuration (use #DirtiesContext on a test class or method to signal that a new Spring app context is required after a specific test).
If you feel you need to run an actual app server such as Tomcat within your tests, check maven plugins such as tomcat7-maven-plugin.
Related
I have a client service like this,
#Service
public class PersonClientService {
private final String EXTERNAL_API;
private RestTemplate restTemplate;
#Autowired
public PersonClientService(RestTemplate restTemplate, #Value("${person.url}") String apiUrl) {
this.restTemplate = restTemplate;
EXTERNAL_API = apiUrl
}
public ResponseDTO createData(PersonDTO personDTO) throws Exception {
try {
HttpEntity<PersonDTO> input = new HttpEntity<>(personDTO);
ResponseEntity<ResponseDTO> restponseDTO = restTemplate.exchange(EXTERNAL_API, HttpMethod.POST, input, ResponseDTO.class);
return responseDTO.getBody();
} catch(Exception e) {
//catch exception
}
}
}
Now the rest template here that I am using is secured with OAuth2 implementation and it is using client_id and secret with grant_type as client_credentials to generate a token and then using this token as header to call the EXTERNAL_API
I am following this guide here but it's not really helpful since it is using JUnit4 and I am on JUnit5: https://www.baeldung.com/oauth-api-testing-with-spring-mvc
I'm confused. What do you want to test?
The sample you link is achieving controller unit-testing with mockmvc.
They use an annotation which loads security context. As a consequence test security context must be configured for the request to reach controller endpoint.
I don't see any security rules on your service (#PreAuthorize or something) => you don't need any security context, just don't load security config.
If you add security rules you want to unit test, load security config and setup test security context (either explicitly or with something like https://github.com/ch4mpy/spring-addons/tree/master/samples/webmvc-jwtauthenticationtoken/src/test/java/com/c4_soft/springaddons/samples/webmvc_jwtauthenticationtoken)
The call to external service is a complete different story: the external service is running with a different security context than the one attached to your tested service thread). Either:
#MockBean RestTemplate (and configure mock for the Rest call your service is issuing) => unit test
ensure test configuration for RestTemplate and external service points to the same started authorization server, load rest template config, auto wire RestTemplate as normal and let it issue request for real to actual external service (which must be started too) => integration test.
You should not start with integration test. Unit test are for more stable and easier to maintain.
I have looked at a few tutorials, and am not quite sure how to proceed with writing a test case for my controller method using JUnit 5. I have read up on TestRestTemplate and Mock functions, but still feel at a loss as to how to begin. I am using a MySQL database, the application contains two classes Product and Feedback, it has a OneToMany relationship, with the owning entity being Feedback.
How do I write a test for the method below?
#PostMapping("/view/{id}")
public ModelAndView addReview(#PathVariable("id") int id, #RequestParam("review") String review, HttpServletRequest request) {
Product product = dao.findById(id).get();
Feedback feedback = new Feedback(review);
product.getFeedbacks().add(feedback);
feedback.setProduct(product);
dao.save(product);
List<Feedback> fs = product.getFeedbacks();
Collections.sort(fs, new FeedbackComparator());
HttpSession session = request.getSession();
session.setAttribute("fs", fs);
return new ModelAndView("/view").addObject("product", product);
}
There are multiple ways to write a test for your Spring MVC controller endpoints:
Use #WebMvcTest and MockMvc to test only your web-layer in isolation.
For such tests, you usually mock all collaborators (in your case your dao) of your controller. Using MockMvc you can then fire requests against a mocked Servlet environment that Spring Boot creates for you. This way you can ensure e.g. your controller endpoints are properly protected (by Spring Security), your path variables and query parameters are mapped as expected, you're returning the correct HTTP response and headers, your Model contains the default attributes, etc.
You can find further information on how to write tests with MockMvc as part of this guide.
Use #SpringBootTest to populate your whole application context, start the real Servlet container (e.g. Tomcat) and invoke your endpoints with the TestRestTemplate or the WebTestClient over HTTP.
This setup requires all your external infrastructure components (database, messaging queues, external systems, etc.) to be available during test execution. This usually takes quite some time and such tests are not as fast as the first variant. Testcontainers can help you a lot here. This way you can ensure the whole use case is working as expected and write integration tests for your application.
So as a general recommendation you should have both kinds of tests and at least ensure your important happy-paths are working as expected with an integration test. For more low-level checks for your controller, the #WebMvcTest annotation comes quite handy.
I know that there are already similar questions, here and here, regarding this problem but every solution proposed failed to help me. There is also mention to this library in most of those answers but (with all due respect) I would like to avoid depending on an external library just to be able to test a simple controller.
So, I have a very simple api that is accessed using a bearer token generated by keycloak and I would like to test the controller. Something along these lines:
#Test
#DisplayName("Should be ok")
#WithMockUser
void whenCalled_shouldBeOk() throws Exception {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
mockMvc.perform(
post("/api/url/something")
.content("{}")
.contentType(APPLICATION_JSON)
.with(authentication(authentication))
).andExpect(status().isOk());
}
The problem is that I will always get a null pointer exception thrown by the KeycloakDeploymentBuilder for it's missing the adapter config. In our SecurityConfig we extend the KeycloakWebSecurityConfigurerAdapter and do all the required configurations for the app to work but I am failing to mock/by-pass this process in the test. Normally I find my way around this authentication problems in the tests (when keycloak isn't used) with #WithMockUser annotation but not this time.
Isn't there way to mock the adapter or the filter process in order to by-pass this issue?
I have tried everything that was answered in the other questions (except the library) with no luck. If you have any clue that could be of help or at least point me in the correct direction (since this can be due to a lack of knowledge on spring security from my part) that would very much appreciated.
2023 update
The deprecated Keycloak adapters for Spring (where KeycloakAuthenticationToken is defined) are not compatible with spring-boot 3. Alternatives in the accepted answer to "Use Keycloak Spring Adapter with Spring Boot 3"
Original answer
As I already wrote in my answer to the first question you linked, #WithMockUser populates the security context with a UsernamePaswordAuthenticationToken when your code / conf probably expect a KeycloakAuthenticationToken.
If you read carefully the same answer, you'll find an alternative to using my lib to do this: manually populate the security-context with a KeycloakAuthenticationToken instance or mock in each test.
Minimal sample with Mockito I added to my repo:
#Test
public void test() {
final var principal = mock(Principal.class);
when(principal.getName()).thenReturn("user");
final var account = mock(OidcKeycloakAccount.class);
when(account.getRoles()).thenReturn(Set.of("offline_access", "uma_authorization"));
when(account.getPrincipal()).thenReturn(principal);
final var authentication = mock(KeycloakAuthenticationToken.class);
when(authentication.getAccount()).thenReturn(account);
// post(...).with(authentication(authentication))
// limits to testing secured #Controller with MockMvc
// I prefer to set security context directly instead:
SecurityContextHolder.getContext().setAuthentication(authentication);
//TODO: invoque mockmvc to test #Controller or test any other type of #Component as usual
}
Maybe, after you mesure how much this clutters your tests (there are very few claims set here) you'll reconsider using my lib (or copying from it as it's opensource).
With my annotation, above sample becomes:
#Test
#WithMockKeycloakAuth
public void test() throws Exception {
//TODO: invoque mockmvc to test #Controller or test any other type of #Component as usual
}
Regarding spring test config with Keycloak involved, you could dig a bit into the tests of spring-addons-keycloak module. You'd find a complete app using KeycloakAuthenticationToken with unit tests (and working test conf).
Last (and maybe off-topic) you could read the repo main README and consider using a more generic OIDC implementation than Keycloak's one. I provide one, along with test annotation, and wrote tutorials on how to extend it to your app specific needs.
I'm trying to deal with Rest api tests.
Well, the controller in my code connects to the external api parsing and returns in the form of JSON.
I'm trying to run a test so that it returns the result of my application’s logic.
Unfortunately, during testing I can't connect with api github.
java.lang.AssertionError: Status expected:<200> but was:<404>
Expected :200
Actual :404
#WebMvcTest(RepositoryDetailsController.class)
#ContextConfiguration(classes = RepositoryDetailsController.class)
class RepositoryDetailsControllerTestt {
#Autowired
private MockMvc mvc;
#MockBean
private RepositoryDetailsService service;
#InjectMocks
private RepositoryDetailsController repositoryDetailsController;
#Before
public void setUp() {
mvc = MockMvcBuilders.standaloneSetup(repositoryDetailsController).build();
}
#Test
public void mockTest() throws Exception {
RepositoryDetailsResponse details = new RepositoryDetailsResponse();
details.setDescription("Ruby toolkit for the GitHub API");
details.set"https://github.com/octokit/octokit.rb.git");
details.setStars("57892");
details.setName("octokit/octokit.rb");
mvc.perform(get("repository/{owner}/{repository}","octokit","octokit.rb")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$.*", hasSize(5)))
.andExpect(jsonPath("$.fullName").value(details.getName()))
.andExpect(jsonPath("$.description").value(details.getDescription()))
.andExpect(jsonPath("$.cloneUrl").value(details.getUrl()))
.andExpect(jsonPath("$.stars").value(details.getStars()));
}
If you can avoid it you usually don't want to connect to an external API in your tests. Having an external API test dependency makes life harder e.g. what if the API is down or what if the API had a breaking change?
There are few tools that you can use to mock this external API, hardcoding the expected responses while still making the request:
Wiremock - very generic, exposes a mock server that can be used with any library that makes the API call
Spring's MockRestServiceServer - especially useful if your code is using RestTemplate to make the API call
Test Containers - if you have a Docker image that provides a mock API
In your example it looks like you attempted to use MockMvc instead of MockRestServiceServer but there is not enough code to tell what you are doing exactly.
Collegues, I have a test:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("dev")
public class ServiceIFTest {
#Autowired
ServiceIF ServiceIF; /*this is web service endpoint bean */
#Test
public void createMonIntBalTranDoc() {
TranDocReq parameter = new TranDocReq();
/*set variables*/
CreateTranDocRes tranDoc = serviceIF.createTranDoc(parameter);
assertNotEquals(java.util.Optional.ofNullable(tranDoc.getId()), 0);
}
}
it is a simple Junit test where I check class field value.
But I need to do an integration test, I would like to check an XML which service will return to the client.
At the current moment if found a quite old article https://www.ibm.com/developerworks/webservices/tutorials/ws-soa-autotest2/ws-soa-autotest2.html
where the author recommends writing client etc...
So the question is how to test integration between soap web service and client from Java project?
In other words, I need to check the XML response returned by my web service.
P.s. Some years ago I did it with SOAP UI and groovy. Also, I tried to use jaxbToString converter, but it cut namespaces.