I have a method in service layer to connect to a service and i am using IHttpClientFactory. My method is working fine. Now i am trying to write unit test cases for the same.
public async Task<MyObject> MyMethodAsync(string arg1, string arg2)
{
var client = _httpClientFactory.CreateClient("XYZ");
var Authkey = "abc";
var AuthToken = "def";
var headers = new Dictionary<string, string>
{
{ Authkey,AuthToken }
};
client.AddTokenToHeader(headers); //This method set the DefaultRequestheader from the dictionary object
var reqData = new
{
prop1 = "X",
prop2 = "Y"
};//req object
var content = new StringContent(JsonConvert.SerializeObject(reqData), Encoding.UTF8, "application/json");
//This is httpClient Post call for posting data
HttpResponseMessage response = await client.PostAsync("postData", content);
if (!response.IsSuccessStatusCode || response.Content == null)
{
return null;
}
MyObject myObject = JsonConvert.DeserializeObject<MyObject>(response.Content.ReadAsStringAsync().Result);//read the result to an object
return myObject;
}
For the above method i am writing test cases. Here i am trying to set the Post methods out put a status code OK and expecting the
MyMethodAsync method to be true as the PostAsync is true. Here i am getting an exception
System.InvalidOperationException : An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set.
[Test]
public async Task MyMethodAsync_Gets_True()
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("It worked!")
};
//Mock the httpclientfactory
var _httpMessageHandler = new Mock<HttpMessageHandler>();
var mockFactory = new Mock<IHttpClientFactory>();
//Specify here the http method as post
_httpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync",
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Post),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
});
var httpClient = new HttpClient(_httpMessageHandler.Object);
mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient);
var arg1 = "X";
var arg2 = "D101";
var service = new MyService(_mockAppSettings.Object, mockFactory.Object);
var result = await service.MyMethodAsync(arg1, arg2);
// Assert
Assert.IsNotNull(result);
}
Can someone show what mistake i am doing here?
As the exception says you have to
either call the PostAsync with an absolute url
or set the BaseAddress of the HttpClient
If you choose the second one all you need to do is this:
var httpClient = new HttpClient(_httpMessageHandler.Object);
httpClient.BaseAddress = new Uri("http://nonexisting.domain"); //New code
mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient);
With this modification the exception will be gone.
But your test will fail because the response.Content will be null and that's why MyMethodAsync will return with null.
To fix this let's change the Setup to this:
public static async Task MyMethodAsync_Gets_True()
{
//Arrange
MyObject resultObject = new MyObject();
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(JsonConvert.SerializeObject(resultObject))
};
var _httpMessageHandler = new Mock<HttpMessageHandler>();
_httpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync",
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Post),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(response);
...
//Act
var result = await service.MyMethodAsync(arg1, arg2);
//Assert
Assert.NotNull(result);
}
Related
Well... I read A LOT of questions here in StackOverflow, but still didn't get answer for it, I have this Web API controller:
public class ERSController : ApiController
{
[HttpGet]
public HttpResponseMessage Get()
{
var resposne = new HttpResponseMessage(HttpStatusCode.OK);
resposne.Content = new StringContent("test OK");
return resposne;
}
[HttpPost]
public HttpResponseMessage Post([FromUri]string ID,[FromBody] string Data)
{
var resposne = new HttpResponseMessage(HttpStatusCode.OK);
//Some actions with database
resposne.Content = new StringContent("Added");
return resposne;
}
}
and I wrote a small tester to it:
static void Main(string[] args)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:54916/");
client.DefaultRequestHeaders.Accept.Clear();
var content = new StringContent("<data>Hello</data>", Encoding.UTF8, "application/json");
var response = client.PostAsync("api/ERS?ID=123", content);
response.ContinueWith(p =>
{
string result = p.Result.Content.ReadAsStringAsync().Result;
Console.WriteLine(result);
});
Console.ReadKey();
}
I always get NULL on the parameter Data in the API.
I tried adding those lines to the tester:
client.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
still NULL, I also replace the content with:
var values = new Dictionary<string, string>();
values.Add("Data", "Data");
var content = new FormUrlEncodedContent(values);
still NULL.
I tried switching the request to:
WebClient client = new WebClient();
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
var values = new NameValueCollection();
values["Data"] = "hello";
var task = client.UploadValuesTaskAsync("http://localhost:54916/api/ERS?ID=123", values);
task.ContinueWith((p) =>
{
string response = Encoding.UTF8.GetString(p.Result);
Console.WriteLine(response);
});
but debugger still saying 'NO!' the Data is still NULL.
I do get the ID with no problem.
If you want to send it as a JSON string, you should do this (using Newtonsoft.Json):
var serialized = JsonConvert.SerializeObject("Hello");
var content = new StringContent(serialized, Encoding.UTF8, "application/json");
You almost got it right with FormUrlEncodedContent, what you had to do was send it with an empty name, like in this example:
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("", "Hello")
});
var response = client.PostAsync("api/ERS?ID=123", content);
I am calling an API Post method, however, I am not sure what I am doing wrong but the value in the API is always null. The method I am calling the API from is below. When I hit this I can see Ids is list of ints with 5 values for example.
private void Save(List<int> Ids)
{
var myAPI = ConfigurationManager.AppSettings["MyAPI"];
string myIds = string.Join(",", Ids);
using (var client = new HttpClient())
{
int result = client.PostAsync(myAPI, new { test = myIds }, new JsonMediaTypeFormatter())
.Result
.Content
.ReadAsAsync<int>()
.Result;
}
}
My API signature is like below - with a breakpoint on I can see it is getting hit but test the parameter I am trying to pass is always null
[HttpPost]
[Route("api/MyController/SaveData")]
public HttpResponseMessage SaveData([FromBody]List<string> test)
{
try
{
//Rest of method removed for brevity
I have tried removing the [FromBody] Annotation from the WebAPI controller but test still is getting null with breakpoint in the SaveData API method
Try this:
private void Save(List<int> Ids)
{
var myAPI = ConfigurationManager.AppSettings["MyAPI"];
using (var client = new HttpClient())
{
var requestBody = JsonConvert.SerializeObject(Ids);
var postRequest = new StringContent(requestBody, Encoding.UTF8, "application/json");
var response = client.PostAsync(myAPI, postRequest).GetAwaiter().GetResult();
var rawResponse = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
// Do something with the answer
}
}
I also suggest to make the method private Task Save and replace .GetAwaiter().GetResult(); with await in front of that calls.
In my case i used System.Web.Http.ApiController instead of System.Web.Mvc.Controller. So over all code looks like
public class YourAppController : ApiController
{
[System.Web.Http.Route("publish-message")]
public HttpResponseMessage Post([System.Web.Http.FromBody] string msges)
{
//Your Code
return Request.CreateResponse(HttpStatusCode.OK, "");
}
}
public async Task<string> PublishMessageCall(string publishMessage){
var returnval = "";
string httpWebRqst = "http://localhost:543134535/publish-message";
using (HttpClient myClient = new HttpClient())
{
var jsonString = JsonConvert.SerializeObject(publishMessage);
var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
var response = await myClient.PostAsync(httpWebRqst, content);
var responseString = await response.Content.ReadAsStringAsync();
}
return await Task.FromResult(returnval);}
I'm trying to do basically what the title says in order to unit test my api controller, but I have problems finding the proper way and can't afford spending too much time on this. Here is my code.
[TestMethod]
public void Should_return_a_valid_json_result()
{
// Arrange
Search search = new Search();
search.Area = "test";
string json = JsonConvert.SerializeObject(search);
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
request.Setup(r => r.HttpMethod).Returns("POST");
request.Setup(r => r.InputStream.ToString()).Returns(json);
context.Setup(c => c.Request).Returns(request.Object);
var controller = new UserController();
controller.ControllerContext = new HttpControllerContext() { RequestContext = context };
//more code
}
Last line returns Error CS0029 Cannot implicitly convert type
'Moq.Mock System.Web.HttpContextBase' to
'System.Web.Http.Controllers.HttpRequestContext'.
I am also not sure about the Moq syntax I should use, other questions,examples and Moq Documentation didn't help me much.
I'm using this approach
var json = JsonConvert.SerializeObject(request);
var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
var httpContext = new DefaultHttpContext()
{
Request = { Body = stream, ContentLength = stream.Length }
};
var controllerContext = new ControllerContext { HttpContext = httpContext };
var controller = new Your_Controller(logic, logger) { ControllerContext = controllerContext };
No need for mock here if the intention is just to pass a request.
[TestMethod]
public void Should_return_a_valid_json_result() {
// Arrange
var search = new Search();
search.Area = "test";
var json = JsonConvert.SerializeObject(search);
var request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.Content = new StringContent(json);
var controller = new UserController();
controller.Request = request;
//more code
}
I want to send a http post that can take a couple of seconds to reply without freezing my UI, currently this code just hangs my application when the method is callled.
What am I doing wrong and how do i achieve my goal?
private async Task<string> DoHttpClientPost(string method, IDictionary<string, object> args = null)
{
{
HttpClientHandler handler = new HttpClientHandler()
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
handler.Proxy = null;
HttpResponseMessage response;
using (var myHttpClient = new HttpClient(handler))
{
myHttpClient.DefaultRequestHeaders.ExpectContinue = false;
myHttpClient.DefaultRequestHeaders.Add("Accept-Charset", "ISO-8859-1,utf-8");
myHttpClient.DefaultRequestHeaders.Add(APPKEY_HEADER, CustomHeaders.GetValues(APPKEY_HEADER));
myHttpClient.DefaultRequestHeaders.Add(SESSION_TOKEN_HEADER, CustomHeaders.GetValues(SESSION_TOKEN_HEADER));
myHttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json-rpc"));
var call = new JsonRequest { Method = method, Id = 1, Params = args };
var jsonObject = JsonConvert.Serialize<JsonRequest>(call);
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json-rpc");
response = await myHttpClient.PostAsync(new Uri(EndPoint), content);
}
Console.WriteLine("\nCalling: " + method + " With args: " + JsonConvert.Serialize<IDictionary<string, object>>(args));
string jsonResponse = await response.Content.ReadAsStringAsync();
return jsonResponse;
}
}
public T Invoke<T>(string method, IDictionary<string, object> args = null)
{
if (method == null)
throw new ArgumentNullException("method");
if (method.Length == 0)
throw new ArgumentException(null, "method");
var jsonString = DoHttpClientPost(method, args).Result;
var jsonResult = JsonConvert.Deserialize<JsonResponse<T>>(jsonString);
return jsonResult.Result;
}
var jsonString = DoHttpClientPost(method, args).Result;
This is your culprit. If you call .Result on a Task from the UI thread it will hang.
You'll need to async all the way up - so Invoke should be async and return a Task<T> and await the DoHttpClientPost call, the caller should be async etc. etc. etc.
You have to make two changes
Modify this line from
response = await myHttpClient.PostAsync(new Uri(EndPoint), content);
to
response = await myHttpClient.PostAsync(new Uri(EndPoint), content).ConfigureAwait(false);
And looks like your intention is to wait for the post call to complete and return the results, so modify this line from
var jsonString = DoHttpClientPost(method, args).Result;
to
var jsonStringTask = DoHttpClientPost(method, args);
jsonStringTask.Wait(); //wait for http post call to complete.
var jsonString = jsonStringTask.Result;
public async static Task<RootObject> GetWeather(string username, string password)
{
var http = new HttpClient();
var response = await http.GetAsync(postURI);
var result = await response.Content.ReadAsStringAsync();
var serializer = new DataContractJsonSerializer(typeof(RootObject));
var ms = new MemoryStream(Encoding.UTF8.GetBytes(result));
var datax = (RootObject)serializer.ReadObject(ms);
return datax;
}
I have the necessary models ready and I made the function call with some hard-coded data to test but its not working.