In my Springboot-Controller my Requestmapping method should support flexible subdirectory names. For that I like to use real regexpression. I have tested my regex and it works fine outside of SpringBoot and RequestMapping, however on top of RequestMapping it does not work.
If any http-requests gets in with
http://dir1 or http://dir2
my Method getFile should be called, but it doesn't.
#RequestMapping(value = "{reg:\\/(dir1|dir2)+}", method = RequestMethod.GET)
public void getFile(HttpServletResponse response, HttpServletRequest requ) throws IOException {
}
My question is how to accomplish this ....
The regex-based #RequestHandler can be achieved through (for more)
#RequestMapping(value = "{reg:regexPattern}", method = RequestMethod.GET)
public void getFile(HttpServletRequest request, HttpServletResponse response,
#PathVariable String reg) throws IOException {
...
}
But in your case, the regex pattern is a directory value that contains Slash("/") makes request handler difficult to find the exact mapping. Instead of #PathVariable, you can use #RequestParam
#RequestMapping(value = "\", method = RequestMethod.GET)
public void getFile(HttpServletRequest request, HttpServletResponse response,
#RequestParam("dir") String dir) throws IOException {
...
}
The solution is (Dirk Deyne gave me the hint) that the Slashes have to be outside the regexpression. Here is the solution, where I have extended the value with another subdir filedir to make it more clear:
#RequestMapping(value = "/{subdir:[dir1|dir2]+}/filedir", method = RequestMethod.GET)
public void getFile(HttpServletResponse response, HttpServletRequest requ) {
...
}
This will serve the following incoming requests:
http://localhost:8080/dir1/filedir
http://localhost:8080/dir2/filedir
Thank you!
Related
I have a service class which looks like -
class A {
#Autowired
RestTemplate restTemplate;
public void method() {
res = restTemplate.postForEntity("http://abc", entity, Employee.class);
resBody = res.getBody();
}
}
Following is the Test Class for it-
class TestA {
#Mock
RestTemplate restTemplate;
#InjectMocks
A obj;
void testMethod1() {
res = ....
when(restTemplate.postForEntity(any(), any(), any()).thenReturn(res);
}
void testMethod2() {
res = ....
when(restTemplate.postForEntity(anyString(), any(), any()).thenReturn(res);
}
}
testMethod1 fails throwing NullPointerException on "res.getBody() from A.method()" while testMethod2 runs successfully
Why is any() not working here while anyString() works? I thought any() works for any data type.
Take a look into the Javadocs for RestTemplate. There are three postForEntity methods:
postForEntity(String url, Object request, Class<T> responseType, Map<String,?> uriVariables)
postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
postForEntity(URI url, Object request, Class<T> responseType)
Your mock in testMethod2 catches for sure one of the first both methods. However the mock in your testMethod1 seems to cover the method with an URI as first parameter and well, therefore your restTemplate.postForEntity("http://abc", entity, Employee.class) does not match.
If you are interested in what method is currently mocked, just type a line, e. g. restTemplate.postForEntity(any(), any(), any()) and then just hover over the method in your favourite IDE to see already at compile time, which exact (overridden) method was covered.
My problem was similar, casting null to String worked for me:
when(restTemplateMock.postForEntity((String)isNull(), any(), eq(List.class)))
.thenReturn(responseEntityMock);
I have a spring-boot application which calls some third party URL (let's say http://example.com/someUri) using webclient(I have used application-dev.properties for injecting this url in my application to achieve loose coupling) and consumes the response and use it in my application.
It's my first time when I am going to write test cases for webclient. and there I used #SprintBootTest.
I found that there are two ways where I can test my webclient with third party Api call by mocking the api call and make it call to my local url(which will be using url(http://localhost:{portNumber}/someUri) from my testing properties file: src/test/resources/application.properties) where It will be giving some mockedResponse in return to my real client:
Using wiremock
Using MockWebServer
consider above code for better understanding:
#Service
Class SampleService{
#Value("${sample.url}")
private String sampleUrl;
public String dummyClient() {
String sample =webClient.get()
.uri(sampleUrl)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.bodyToMono(String.class)
.block();
return sample;
}
}
application-dev.properties:
sample.url:http://example.com/someUri
src/test/resouces/application.properties:
http://localhost:8090/someUri
Testing class:
#SpringBootTest
public class sampleTestingClass {
#Autowired
private SampleService sampleService;
#Value("${sample.url}")
private String sampleUrl;
public static MockWebServer mockWebServer = new MockWebServer();
#BeforeAll
static void setUp() throws IOException {
mockWebServer.start(8090);
}
#AfterAll
static void tearUp() throws IOException {
mockWebServer.close();
}
HttpUrl url = mockWebServer.url("/someUri");
mockWebServer
.enqueue(
new MockResponse()
.setResponseCode(200)
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody("Sample Successful"));
String sample = sampleService.dummyClient();
assertEquals(sample ,matches("Sample Successful"));
}
}
but this code isn't working. it's giving me above error:
java.lang.NullPointerException
It will be really helpful if anybody knows how this can be fixed to achieve my unit testing using mocked Url? Thanks in advance!
Here is a working example:
#Component
public class QuotesClient {
private final WebClient webClient;
public QuotesClient(WebClient.Builder builder, #Value("${client.baseUrl}") String baseUrl) {
this.webClient = builder.baseUrl(baseUrl).build();
}
public JsonNode getData() {
return this.webClient
.get()
.retrieve()
.bodyToMono(JsonNode.class)
.block();
}
}
Using the WebClient.Builder is optional.
The corresponding test can look like the following:
class QuotesClientTest {
private QuotesClient quotesClient;
private MockWebServer server;
#BeforeEach
public void setup() {
this.server = new MockWebServer();
this.quotesClient = new QuotesClient(WebClient.builder(), server.url("/").toString());
}
#Test
public void test() {
server.enqueue(new MockResponse()
.setStatus("HTTP/1.1 200")
.setBody("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}")
.addHeader("Content-Type", "application/json"));
JsonNode data = quotesClient.getData();
assertNotNull(data);
System.out.println(data);
}
}
If you are searching for a similar setup using WireMock, Spring Boot, and JUnit 5, take a look at the linked guide.
I'm testing an endpoint and the response content-type is "application/xml;charset=ISO-8859-1", when I expect it to be "application/xml". Can you see where I may have misconfigured the produces aspect? I added it to the #RequestMapping for the function and received the same, unexpected, result.
Feature Under Test
#Controller
#RequestMapping(value = "/sitemaps",
consumes = MediaType.ALL_VALUE,
produces = MediaType.APPLICATION_XML_VALUE)
public class SitemapQueryControllerImpl implements SitemapQueryController {
#RequestMapping(value = "/index.xml", method = RequestMethod.GET)
public ResponseEntity<String> GetSitemapIndex() {
return new ResponseEntity<>("<Hello>", HttpStatus.OK);
}
}
Test
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = SitemapQueryControllerImpl.class, secure = false)
#ContextConfiguration(classes = {ApplicationTestContext.class})
public class SitemapQueryController_Spec {
#Autowired
private MockMvc mockMvc;
#Before
public void setup() { }
#Test
public void GetSitemapIndex_Successul() throws Exception {
String expect = "<Hello>";
mockMvc.perform(get("/sitemaps/index.xml")
.contentType(MediaType.APPLICATION_XML_VALUE))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_XML_VALUE))
.andExpect(content().xml(expect));
}
By default charset is UTF-8, MappingJackson2HttpMessageConverter is who manages the charSet. You can override by implementing the bean and setting charSet to null.
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
jsonConverter.setObjectMapper(objectMapper);
jsonConverter.setDefaultCharset(null);
return jsonConverter;
}
If you just use application/xml as your produces/accepts configuration (as is done through the method you use), it uses the default charset, which for compatibility reasons is set to the ISO charset you're getting returned. I've encountered the same issue today and yesterday, and the solution is to explicitly pass application/xml;charset=utf-8 as contentType and accept headers, and check that you get application/xml;charset=utf-8 as the contentType of the returned content. The easiest way to do this is to construct it using the new MediaType(MediaType.APPLICATION_XML.getType(), MediaType.APPLICATION_XML.getSubType(), StandardCharSets.UTF_8) constructor to create a new MediaType for application/xml;charset=UTF-8 that you can then use in your test requests.
I have a controller method as follows
#RequestMapping(value = "/{fruitName:Mango|Orange|Grapes}", method = RequestMethod.GET)
public String viewFruit(ModelMap model, #PathVariable String fruitName) {
...
}
Here I am hard-coding Mango, Orange and Grapes, such that if url matches with any of these, then only this method should execute.
What I want to do is that, instead of hard-coding these fruits here. I want to declare them in a properties file and by using SPEL, I should bring it here.
Is this possible to do?
I tried code below for this, but failed with error:
#RequestMapping(value = "/{fruitName:#{fruit['seasonFruit.requestMapping']}}", method = RequestMethod.GET)
public String viewFruit(ModelMap model, #PathVariable String fruitName) {
...
}
For following properties File (fruit.properties)
seasonFruit.requestMapping = Mango|Orange|Grapes
Please suggest, how this could be achieved?
#ArtemBilan So, isn't there any way by which we could achieve this? By separation of hard coding
Well, I hope you can do that from code:
#Value("#{ environment['seasonFruit.requestMapping'] }")
private String fruitName;
#RequestMapping(value = "/{fruitName:[a-z]", method = RequestMethod.GET)
public String viewFruit(ModelMap model, #PathVariable String fruitName) {
if (fruitName.matches(this.fruitName))
}
You may not be able to use combination of REGEX and values loaded from property files in requestMapping directly. But i am able to achieve in a sample app. Essentially i am resolving the entire regex pattern from property file, not just values of it. RequestMapping can resolve values from properties file.Hope this helps.
Property file
sample.test={url:mango|orange}
Controller method:
#RequestMapping(value = { "/example/${sample.test}" }, method = RequestMethod.GET)
public String testPattern(#PathVariable String url) {
System.out.println("url:" + url);
return sample;
}
As i just started Mockito, I have below method that i want to test. In my YouTubeChannelImporter.java file there are both methods : readJsonFromUrl(String url) and readAll(rd)
public JSONObject readJsonFromUrl(String url) throws IOException,
JSONException {
InputStream is = new URL(url).openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is,
Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JSONObject json = new JSONObject(jsonText);
return json;
} finally {
is.close();
}
}
I started with some rough idea.Below is the sample test case.
#Test
public void readJsonFromUrl() throws IOException, JSONException {
String urlTest="http://google.com/api";
YouTubeChannelImporter mockYoutube = mock(YouTubeChannelImporter.class);
YouTubeChannelImporter tubeChannelImporter =new YouTubeChannelImporter();
// URL url=new URL(urlTest);
// InputStream inputStream = mock(InputStream.class);
// when(url.openStream()).thenReturn(inputStream);
BufferedReader rd=mock(BufferedReader.class);
when(mockYoutube.readAll(rd)).thenReturn("{\"kind\":\"you\"}");
String jsonText="{\"kind\":\"you\"}";
JSONObject object=new JSONObject(jsonText);
assertEquals("{\"kind\":\"you\"}",tubeChannelImporter.readJsonFromUrl(urlTest).toString());
}
My issue is when i will test the function readJsonFromUrl(String url) then readAll(rd) should not execute. Rather mock should be in action here. I am aware this issue is because tubeChannelImporter.readJsonFromUrl(urlTest).toString() . Looking for any other way to achieve my goal.
Thanks
You're mocking one instance of your class, and then executing another - as you noticed, this does not achieve the intended result.
I think the easiest way of testing your method is with spying ("partial mocking"). With this technique, you'll have an actual instance of your class with only a single method, readAll mocked out:
#Test
public void readJsonFromUrl() throws IOException, JSONException {
// Set up the spied instance:
YouTubeChannelImporter tubeChannelImporter = spy(new YouTubeChannelImporter());
doReturn("{\"kind\":\"you\"}").when(tubeChannelImporter).readAll(any(Reader.class));
// Test the execution
String urlTest="http://google.com/api";
String jsonText="{\"kind\":\"you\"}";
JSONObject object=new JSONObject(jsonText);
assertEquals("{\"kind\":\"you\"}",tubeChannelImporter.readJsonFromUrl(urlTest).toString());
}
Personally, for a test like this where you have to read a file and there's not much else going on, I would read an actual file rather than mock out a BufferedReader. Then you just have to Assert on the JSONObject output from the call to readJsonFromUrl().
Your problems stem from trying to test against the private method readAll(). Try to test only your public methods in your tests (e.g. readJsonFromUrl()) and you'll find you have to jump through less hoops in your test code.
If your class already implements an Interface, then just write tests against the Interface.
If you find that you have a lot of private methods, perhaps your class is responsible for doing too much work, so factor methods out into their own class(es). Then you can test them in isolation.
If you have a maven project, the place to put your test files, to read from, is in /src/test/resources