I have a org.springframework.web.server.WebFilter that I am testing. I am mocking the ServerWebExchange to return a MockServerHttpResponse. When the web filter wants to return an error response it uses
private static Mono<Void> setErrorResponse(ServerWebExchange exchange, String message) {
ServerHttpResponse response = exchange.getResponse()
response.statusCode = HttpStatus.BAD_REQUEST
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE)
DataBuffer buffer = response.bufferFactory().wrap(message.getBytes("UTF-8"))
return response.writeWith(Flux.just(buffer))
}
Then I try to get the the response body by calling
response.getBodyAsString().block()
But I always get
java.lang.IllegalStateException: No content was written nor was setComplete() called on this response.
How do you use the mock to get the response body that does not seem to get set when using writeWith()?
Related
I want to get the size of an http:/.../file before I download it. The file can be a webpage, image, or a media file. Can this be done with HTTP headers? How do I download just the file HTTP header?
Yes, assuming the HTTP server you're talking to supports/allows this:
public long GetFileSize(string url)
{
long result = -1;
System.Net.WebRequest req = System.Net.WebRequest.Create(url);
req.Method = "HEAD";
using (System.Net.WebResponse resp = req.GetResponse())
{
if (long.TryParse(resp.Headers.Get("Content-Length"), out long ContentLength))
{
result = ContentLength;
}
}
return result;
}
If using the HEAD method is not allowed, or the Content-Length header is not present in the server reply, the only way to determine the size of the content on the server is to download it. Since this is not particularly reliable, most servers will include this information.
Can this be done with HTTP headers?
Yes, this is the way to go. If the information is provided, it's in the header as the Content-Length. Note, however, that this is not necessarily the case.
Downloading only the header can be done using a HEAD request instead of GET. Maybe the following code helps:
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://example.com/");
req.Method = "HEAD";
long len;
using(HttpWebResponse resp = (HttpWebResponse)(req.GetResponse()))
{
len = resp.ContentLength;
}
Notice the property for the content length on the HttpWebResponse object – no need to parse the Content-Length header manually.
Note that not every server accepts HTTP HEAD requests. One alternative approach to get the file size is to make an HTTP GET call to the server requesting only a portion of the file to keep the response small and retrieve the file size from the metadata that is returned as part of the response content header.
The standard System.Net.Http.HttpClient can be used to accomplish this. The partial content is requested by setting a byte range on the request message header as:
request.Headers.Range = new RangeHeaderValue(startByte, endByte)
The server responds with a message containing the requested range as well as the entire file size. This information is returned in the response content header (response.Content.Header) with the key "Content-Range".
Here's an example of the content range in the response message content header:
{
"Key": "Content-Range",
"Value": [
"bytes 0-15/2328372"
]
}
In this example the header value implies the response contains bytes 0 to 15 (i.e., 16 bytes total) and the file is 2,328,372 bytes in its entirety.
Here's a sample implementation of this method:
public static class HttpClientExtensions
{
public static async Task<long> GetContentSizeAsync(this System.Net.Http.HttpClient client, string url)
{
using (var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url))
{
// In order to keep the response as small as possible, set the requested byte range to [0,0] (i.e., only the first byte)
request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(from: 0, to: 0);
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
if (response.StatusCode != System.Net.HttpStatusCode.PartialContent)
throw new System.Net.WebException($"expected partial content response ({System.Net.HttpStatusCode.PartialContent}), instead received: {response.StatusCode}");
var contentRange = response.Content.Headers.GetValues(#"Content-Range").Single();
var lengthString = System.Text.RegularExpressions.Regex.Match(contentRange, #"(?<=^bytes\s[0-9]+\-[0-9]+/)[0-9]+$").Value;
return long.Parse(lengthString);
}
}
}
}
WebClient webClient = new WebClient();
webClient.OpenRead("http://stackoverflow.com/robots.txt");
long totalSizeBytes= Convert.ToInt64(webClient.ResponseHeaders["Content-Length"]);
Console.WriteLine((totalSizeBytes));
HttpClient client = new HttpClient(
new HttpClientHandler() {
Proxy = null, UseProxy = false
} // removes the delay getting a response from the server, if you not use Proxy
);
public async Task<long?> GetContentSizeAsync(string url) {
using (HttpResponseMessage responce = await client.GetAsync(url))
return responce.Content.Headers.ContentLength;
}
I m new to Mockito and trying to mock the webservice responses, I did tried mocking at some extent few Objects got worked, But the end mocked WebResponse is always returning null.
Service Method i am going to test:getWebResponse Method
public WebResponse getWebResponse(String crmNumber) throws JSONException, ExecutionException, WebException {
Map<String, String> HEADERS_POST = new HashMap<String, String>() {
{
put(WebUtil.HEADER_CONTENT, WebUtil.CONTENT_JSON);
put(WebUtil.HEADER_ACCEPT, WebUtil.CONTENT_JSON);
}
};
JSONObject requestJson = new JSONObject();
requestJson.put("crmNumber", crmNumber);
requestJson.put("application", "ABCD");
requestJson.put("feature", "DDDFL");
// Using internal web service becuase device authentication is done separately.
String url = CommonUtil.getServiceBaseUrl(true) + "/ett";
WebServiceClient client = WebServiceClientRegistry.getClient(ApacheCustom.class);
WebRequest webReq = new GenericWebRequest(WebRequestMethod.POST, url, HEADERS_POST, requestJson.toString());
// Till here i m getting all mocked object (client also Mocked) after this stament the webRes is returning null;
WebResponse webRes = client.doRequest(webReq);
return webRes;
}
And here the test Method:
#Test
public void getWebResponseTest() {
mockStatic(CommonUtil.class);
mockStatic(WebServiceClientRegistry.class);
this.webResponse = new GenericWebResponse(200, "", new HashMap(), "");
try {
Mockito.when(CommonUtil.getServiceBaseUrl(true)).thenReturn("https://stage.com/service");
WebRequest webReq = new GenericWebRequest(WebRequestMethod.POST, "https://stage.com/service", new HashMap(), "");
Mockito.when(WebServiceClientRegistry.getClient(ApacheCustom.class)).thenReturn(client);
Mockito.when(client.doRequest(webReq)).thenReturn(this.webResponse);
WebResponse wesponse = this.ServiceResponse.getWebResponse("Number");
Assert.assertEquals(wesponse.getStatusCode(), 200);
} catch (Exception e) {
Assert.fail();
}
}
But the getWebResonse method from Test class always returning null Response(Even Though it is mocked)
You mock client.doRequest as follows:
Mockito.when(client.doRequest(webReq)).thenReturn(this.webResponse);
but you create a new instance of WebRequest in your service under test.
You call doRequest with a different argument than recorded in your test.
Arguments are compared with equals.
Most likely WebRequest does not override equals, so recorded interaction is ignored and a default response (null) is rerurned.
I guess WebResuest may not be the code you own (you haven’t specified this in your question), so it may be impossible to override it.
Thus, you can use a different argument matcher.
You can use ArgumentMatchers.any() for good start, or implement a custom argument matcher.
I am facing strange issue where response as string not coming back from one service to another service. we have created microservices where one service is calling another service. i can see response printed in logs . after that line immediately i am returning that response but its coming back as null.
I created similar method with same code and it works fine. I have put code for calling service and service method from which i am returning response.
controller:
#RequestMapping(value = "/test/save", method = RequestMethod.POST)
#ResponseBody
public String save(#RequestBody Calculation calculation,
HttpServletRequest request) {
logger.info("In .save");
String result = "false";
try {
result = CalService.save(calculation);
logger.info("Response from service is :" + result);
} catch (Exception e) {
logger.error("Exception occured in save:", e);
}
return result;
}
method call client :
public String saveCal(Calculation calculation) {
String result = null;
try {
logger.info("In save");
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("REMOTE_USER", "test");
HttpEntity<Calculation> request = new HttpEntity<Calculation>(Calculation, headers);
RestTemplate template = new RestTemplate();
template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
result = template.postForObject(url+"/test/save", request, String.class);
} catch (Exception e) {
logger.error("Exception occured in SaveMddMd", e);
result = "fail";
}
logger.info("Save"+result);
return result;
}
result returned is success or failure.
I can see result printed in controller as success but when it comes back to client it prints as null. I created exact same method with different signature which returns result as success. we are using microservices here.
Jordan
I'm having a bit of trouble with a specific implementation of testing out my Web.API methods using RestSharp. I have been very successful in performing POSTS and GETS in my open (non-secured) methods. However, when I have to send in a token to determine access I have problems.
Here is the implementation:
I am using OWIN middleware for my Web.API. The client must post to a token service in order to get the given Token that contains their claims. All of this has been working fine.
In my test my Initializer has the following code that posts to the token service and gets back the token. This works wonderfully - returns back the token as advertised:
[TestInitialize]
public void SetupTest()
{
_verificationErrors = new StringBuilder();
_client = new RestClient
{
BaseUrl = new Uri(ConfigurationManager.AppSettings["ServicesBaseUrl"])
};
_serviceRequestPrepender = ConfigurationManager.AppSettings["ServiceRequestPrepender"];
// Initialize this by getting the user token put back for all of the tests to use.
var request = new RestRequest(string.Format("{0}{1}", _serviceRequestPrepender, ConfigurationManager.AppSettings["TokenEndpointPath"]), Method.POST);
// Add header stuff
request.AddParameter("Content-Type", "application/x-www-form-urlencoded", ParameterType.HttpHeader);
request.AddParameter("Accept", "application/json", ParameterType.HttpHeader);
// Add request body
_userName = "{test student name}";
_password = "{test student password}";
_userGuid = "{this is a guid value!!}";
_clientIdentifier = ConfigurationManager.AppSettings["ClientIdentifier"];
_applicationId = ConfigurationManager.AppSettings["ApplicationId"];
string encodedBody = string.Format("grant_type=password&username={0}&password={1}&scope={2} {3} {4} {0}"
, _userName, _password, _clientIdentifier, _userGuid, _applicationId);
request.AddParameter("application/x-www-form-urlencoded", encodedBody, ParameterType.RequestBody);
// execute the request
IRestResponse response = _client.Execute(request);
// Make sure everything is working as promised.
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Assert.IsTrue(response.ContentLength > 0);
_token = new JavaScriptSerializer().Deserialize<Token>(response.Content).access_token;
}
Next is the following code that calls a Web.API method which passes the given token along to another Web.API method where I'm performing a GET to extract some information from my service.
[TestMethod]
public void GetUserProfileTest()
{
// Arrange
var request = new RestRequest(string.Format("{0}{1}", _serviceRequestPrepender, "api/UserProfiles/UserProfiles/Get/{appId}/{userId}/{username}"), Method.GET);
// Add header stuff
request.AddParameter("Content-Type", "application/json", ParameterType.HttpHeader);
request.AddParameter("Accept", "/application/json", ParameterType.HttpHeader);
request.AddParameter("Authorization", string.Format("{0} {1}", "Bearer", _token));
request.AddUrlSegment("appId", "1");
request.AddUrlSegment("userId", _userGuid);
request.AddUrlSegment("username", _userName);
// execute the request
IRestResponse response = _client.Execute(request);
// Make sure everything is working as promised.
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Assert.IsTrue(response.ContentLength > 0); // do more when working
}
Next, the service is called, but I have decorated the Web.API method with a custom access security check. This is a VERY simple security check in that it only checks to see if the token is valid and not expired. Here is the IsAuthorized method of that attribute:
protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// Custom Code here
return ValidityChecker.IsTokenValid(actionContext);
}
The ValidityChecker is a simple class that only checks to see if the token is valid:
public class TokenValidityChecker
{
public ClaimsPrincipal PrincipalWithClaims { get; private set; }
/// <summary>
/// Extracts out the ability to perform token checking since all Token checking attributes will need t his.
/// </summary>
/// <param name="actionContext"></param>
/// <returns></returns>
public bool IsTokenValid(System.Web.Http.Controllers.HttpActionContext actionContext)
{
bool result = false;
var principal = actionContext.RequestContext.Principal;
if (principal.GetType() == typeof(ClaimsPrincipal))
{
PrincipalWithClaims = (ClaimsPrincipal)principal;
result = PrincipalWithClaims.Identity.IsAuthenticated;
}
// Custom Code here
return result;
}
}
So, with the background in place - here is the question. As you can see, normally, when the service is called the ValidityChecker will receive an HttpActionContext. Along with that, the RequestContext.Principal of that HttpActionContext will normally be of type ClaimsPrincipal.
However, when running from a unit test and using RestSharp it is, of course, a WindowsPrincipal.
Is there a way using RestSharp to make that a ClaimsPrincipal? I've tried to ensure the token is included in the header using the Authorization parameter, but have not had any luck.
Well - If I would simply read the details of my own code I could have completed this long ago.
The answer was VERY simple. The code in the question adds the token to the parameters, but does not annotate it as HttpHeader. I forgot to put that into the method call. Here is the line that fixed it:
request.AddParameter("Authorization", string.Format("{0} {1}", "Bearer", _token), ParameterType.HttpHeader);
The "ParameterType.HttpHeader" in the method call did the trick.
I have see several questions here in Stackoverflow about out parameters in MOQ, my question is how fill this parameter: Lets to code:
[HttpPost]
public HttpResponseMessage Send(SmsMoRequest sms)
{
if (sms == null)
return Request.CreateResponse(HttpStatusCode.BadRequest);
SmsMoResponse response;
_messageService.Process(sms, out response);
return Request.CreateResponse(HttpStatusCode.Created, response.ToString());
}
I want to test this post:
[Test]
public void Should_Status_Be_Create_With_Valid_XML()
{
// Arrange
var messageServiceMoq = new Mock<IMessageService>();
SmsMoResponse response;
messageServiceMoq.Setup(mock => mock.Process(It.IsNotNull<SmsMoRequest>(), out response));
_kernel.Bind<IMessageService>().ToConstant(messageServiceMoq.Object);
var client = new HttpClient(_httpServer) { BaseAddress = new Uri(Url) };
// Act
using (var response = client.PostAsync(string.Format("Api/Messages/Send"), ValidContent()).Result)
{
// Asserts
response.IsSuccessStatusCode.Should().BeTrue();
response.StatusCode.Should().Be(HttpStatusCode.Created);
}
}
Problem
My response object in Send method (POST) is used in post response but the _messageService.Process is responsible to fill the response object.
In test method Should_Status_Be_Create_With_Valid_XML I mock _messageService.Process and response object is not fill ocorr error Null reference in Request.CreateResponse(HttpStatusCode.Created, response.ToString());
response is null!
Of course response is null, there's no code anywhere that would set it to anything (in your service method or your mock).
Since you're mocking the method that would usually fill it in, it's up to you to specify how it is set. You should fill in the object before calling Setup with what you expect to be the value when that method is called.
Also, see this question for more info.