I have WebApi controller with async method Post:
public class WebApiController : ApiController
{
public async Task<HttpResponseMessage> Post(HttpRequestMessage request)
{
try
{
string json = await request.Content.ReadAsStringAsync();
var model = JsonConvert.DeserializeObject<TModel>(json);
var newEntity = Mapper.Map<TEntity>(model);
var newEntityVersion = Mapper.Map<TEntityVersion>(model);
var result = await CurrentRepository.AddAsync(newEntity, newEntityVersion);
return CreateResponse(result, "DefaultApiPost");
}
catch (Exception e)
{
Logger.Error("Post in " + ControllerName, e);
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, e);
}
}
}
AddAsync method looks like :
public virtual async Task<TEntity> AddAsync(TEntity entity, TEntityVersion version)
{
await DatabaseContext.SaveChangesAsync();
return entity;
}
When I call Post method using HttpClient I can enter AddAsync method :
client.PostAsync(requestUri, content).Result.Content.ReadAsStringAsync().Result;
But when I call Post method using WebApi controller explicit I can not enter AddAsync method, just jump over it :/ :
var product = FakeProduct();
var newEntity = Mapper.Map<ProductEntity>(product);
var newEntityVersion = Mapper.Map<ProductVersionEntity>(product);
productRepository.AddAsync(newEntity, newEntityVersion).Returns(Task.FromResult<ProductEntity>(newEntity));
var content = JsonConvert.SerializeObject(Product);
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:8888/api/Product/")
{
Content = new StringContent(content),
};
var webApiController = FakeWebApiControllerWithPostRoute();
var result = await webApiController.Post(request);
Assert.AreEqual(HttpStatusCode.Created, result.StatusCode);
Any idea why this method is not executed ?
Related
I am trying to UnitTest my MVC Controller method, which internally makes call to an WebAPI(using HttpClient). I'm not able to figure out how can I fake the httpclient call, as it should not go for actual request. Below is my source code and unit test case. Test case fails, as the call goes for actual HttpRequest (An error occurred while sending the request. A connection with the server could not be established)
Base MVC Controller
public class BaseController : Controller
{
public virtual async Task<T> PostRequestAsync<T>(string endpoint, Object obj) where T : class
{
var address = "http://localhost:5001/api/Login";
StringContent json = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "");
using (var client = new HttpClient())
{
try
{
var response = await client.PostAsync(address, json); // Test case fails here
if (response.IsSuccessStatusCode)
{
string data = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(data);
}
return default(T);
}
catch (WebException)
{
throw;
}
}
}
}
Derived class Controller
public class AccountController : BaseController
{
public AccountController() : base()
{
}
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
var result = await PostRequestAsync<ResultObject>(Constants.UserLoginAPI, model); // this is call for basecontroller method which actually has HttpClient call.
var output = JsonConvert.DeserializeObject<UserObject>(result.Object.ToString());
if (result.Succeeded && !string.IsNullOrEmpty(output.Email))
{
var userRoleInfo = await GetRequestAsync<List<UserRoleObject>>(string.Format(Constants.GetUserRoleInfoAPI, output.Email));
if (userRoleInfo != null)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, output.Email),
new Claim("Username", output.UserName)
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties { IsPersistent = model.RememberMe });
}
return View(new LoginViewModel());
}
}
return View(model);
}
}
TestMethod
[Fact]
public async Task LoginTest_Post_UserHasInValidCredentials()
{
// Arrange
var mockModel = new LoginViewModel { };
mockModel.Password = "TestPassword";
mockModel.Email = "test#test.com";
mockModel.RememberMe = false;
var commonResult = new CommonResult { Object = null, Succeeded = false, StatusCode = Common.Enums.ResponseStatusCodeEnum.Success };
var email = string.Empty;
var mockHttp = new MockHttpMessageHandler();
var mockBase = new Mock<BaseController>() { CallBase=true};
mockHttp.When("http://127.0.0.1:5001/*").Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON - using RichardSzalay.MockHttp;
//// Inject the handler or client into your application code
StringContent jsonInput = new StringContent(JsonConvert.SerializeObject(mockModel), Encoding.UTF8, "application/json");
var client = new HttpClient(mockHttp);
var response = await client.PostAsync("http://127.0.0.1:5001" + Constants.UserLoginAPI, jsonInput);
var json = await response.Content.ReadAsStringAsync();
mockBase.Setup(test => test.PostRequestAsync<CommonResult>(Constants.UserLoginAPI, mockModel)).Returns(Task.FromResult(CommonResult()));
var result = await accountController.Login(mockModel); // test case fails, as the call goes for actual HttpRequest (An error occurred while sending the request. A connection with the server could not be established)
//var viewResult = Assert.IsType<ViewResult>(result);
Assert.NotNull(commonResult);
Assert.False(commonResult.Succeeded);
Assert.Empty(email);
//Assert.NotNull(model.Email);
}
Tight coupling to HttpClient in the base controller makes it difficult to test derived classes in isolation. Review and refactor that code to follow DI.
No need to have a base controller and it is usually not advised.
Extract PostRequestAsync out into its own service abstraction and implementation.
public interface IWebService {
Task<T> PostRequestAsync<T>(string endpoint, Object obj) where T : class;
Task<T> GetRequestAsync<T>(string endpoint) where T : class;
}
public class WebService : IWebService {
static HttpClient client = new HttpClient();
public virtual async Task<T> PostRequestAsync<T>(string requestUri, Object obj) where T : class {
var content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "");
try {
var response = await client.PostAsync(requestUri, content); // Test case fails here
if (response.IsSuccessStatusCode) {
string data = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(data);
}
return default(T);
} catch (WebException) {
throw;
}
}
public async Task<T> GetRequestAsync<T>(string requestUri) where T : class {
try {
var response = await client.GetAsync(requestUri);
if (response.IsSuccessStatusCode) {
string data = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(data);
}
return default(T);
} catch (WebException) {
throw;
}
}
}
Refactor derived controllers to depend on the service abstraction
public class AccountController : Controller {
private readonly IWebService webService;
public AccountController(IWebService webService) {
this.webService = webService;
}
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) {
if (ModelState.IsValid) {
var result = await webService.PostRequestAsync<ResultObject>(Constants.UserLoginAPI, model);
if (result.Succeeded) {
var output = JsonConvert.DeserializeObject<UserObject>(result.Object.ToString());
if (output != null && !string.IsNullOrEmpty(output.Email)) {
var userRoleInfo = await webService.GetRequestAsync<List<UserRoleObject>>(string.Format(Constants.GetUserRoleInfoAPI, output.Email));
if (userRoleInfo != null) {
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, output.Email),
new Claim("Username", output.UserName)
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties { IsPersistent = model.RememberMe });
}
return View(new LoginViewModel());
}
}
}
return View(model);
}
}
This should now allow you to mock the dependency when testing in isolation without adverse side effects.
[Fact]
public async Task LoginTest_Post_UserHasInValidCredentials() {
// Arrange
var mockModel = new LoginViewModel { };
mockModel.Password = "TestPassword";
mockModel.Email = "test#test.com";
mockModel.RememberMe = false;
var commonResult = new CommonResult {
Object = null,
Succeeded = false,
StatusCode = Common.Enums.ResponseStatusCodeEnum.Success
};
var mockWebService = new Mock<IWebService>();
var accountController = new AccountController(mockWebService.Object) {
//HttpContext would also be needed
};
mockWebService
.Setup(test => test.PostRequestAsync<CommonResult>(Constants.UserLoginAPI, mockModel))
.ReturnsAsync(commonResult);
//
//Act
var result = await accountController.Login(mockModel);
//Assert
//...Make assertions here
}
I would inject an IHttpClient interface, and in release register a HttpClient wrapper that implements that interface.
I am writing controllers in Web API 2, against which odata queries will be executed:
[Route("", Name = "GetAccount")]
[HttpGet]
public async Task<IHttpActionResult> GetAccount()
{
var query = Request.RequestUri.PathAndQuery.Split('/')[2]; //this query variable will be something "filter=name eq 'alex'"
var response = _accountService.Get(query);
if (!response.Result.IsSuccessStatusCode)
{
return NotFound();
}
var readAsAsync = response.Result.Content.ReadAsAsync<object>();
if (readAsAsync == null)
{
return NotFound();
}
var result = await readAsAsync;
return Ok(result);
}
How do I inject the Request, specifically as it relates to: var query = Request.RequestUri.PathAndQuery.Split('/')[2]; ?
Here's a very basic test that I've written for this controller:
[TestMethod]
public void GetAccount_Returns_IHttpActionResultTask()
{
var accountsService = new Mock<IAccountService>();
var sut = new AccountsController(accountsService.Object);
Assert.IsInstanceOfType(sut.GetAccount(), typeof(Task<IHttpActionResult>));
}
In order to test with different values for Request.RequestUri...., how do I rewrite my controller to be more testable?
Set the Request property on the ApiCntroller.
[TestMethod]
public async Task GetAccount_Returns_IHttpActionResult() {
//Arrange
var accountsService = new Mock<IAccountService>();
var sut = new AccountsController(accountsService.Object);
sut.Request = new HttpRequestMessage {
RequestUri = new Uri("http://localhost/api/accounts?filter=name")
};
//Act
var result = await sut.GetAccount();
//Assert
Assert.IsInstanceOfType(result, typeof(IHttpActionResult));
}
Also there or some potential blocking issues with the method under test. Mixing async/await with .Result blocking calls can cause deadlocks.
Refactor:
[Route("", Name = "GetAccount")]
[HttpGet]
public async Task<IHttpActionResult> GetAccount() {
var query = Request.RequestUri.PathAndQuery.Split('/')[2]; //this query variable will be something "filter=name eq 'alex'"
var response = await _accountService.Get(query);
if (!response.IsSuccessStatusCode) {
return NotFound();
}
var readAsAsync = response.Content.ReadAsAsync<object>();
if (readAsAsync == null) {
return NotFound();
}
var result = await readAsAsync;
return Ok(result);
}
public async Task<HttpResponseMessage> getOne(HttpRequestMessage request, int id)
{
return CreateResponse(async () =>
{
var category = await _unitOfWork.Categories.GetSingleAsync(id);
var categoryVm = Mapper.Map<Category, CategoryViewModel>(category);
HttpResponseMessage response = request.CreateResponse<CategoryViewModel>(HttpStatusCode.OK, categoryVm);
return response;
});
}
Base Class
protected Task<IHttpActionResult> CreateResponse(Func<IHttpActionResult, Task> function)
{
IHttpActionResult response = null;
try
{
response = function.Invoke();
}
}
Read up on Cross cutting concerns.
You are giving yourself unnecessary trouble. Your example can be reduced to :
public async Task<IHttpActionResult> getOne(int id) {
var category = await _unitOfWork.Categories.GetSingleAsync(id);
var categoryVm = Mapper.Map<Category, CategoryViewModel>(category);
return Ok(categoryVm);
}
Try to keep controller lean.
Check this answer
I have a web api which I'm calling (this is working correctly)
I call this like so
public ActionResult Index()
{
var mod = Checksomething();
return View();
}
public async Task Checksomething()
{
try
{
var client = new HttpClient();
var content = new StringContent(JsonConvert.SerializeObject(new UserLogin { EmailAddress = "SomeEmail#Hotmail.com", Password = "bahblah" }));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync("http://localhost:28247/api/UserLoginApi2/CheckCredentials", content);
var value = await response.Content.ReadAsStringAsync();
// I need to return UserProfile
var data = JsonConvert.DeserializeObject<UserProfile[]>(value);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
My web api passes back a model called UserProfile, I'm having great difficulty trying to return data back to the Index controller, would someone please enlighten me.
You need to change your method signature to use the generic version of Task
public async Task<ActionResult> Index()
{
UserProfile[] profiles = await Checksomething();
if (profiles.Any())
{
var user = profiles.First();
string username = user.FirstName;
// do something w/ username
}
return View();
}
public async Task<UserProfile[]> Checksomething()
{
try
{
var client = new HttpClient();
var content = new StringContent(JsonConvert.SerializeObject(new UserLogin { EmailAddress = "SomeEmail#Hotmail.com", Password = "bahblah" }));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync("http://localhost:28247/api/UserLoginApi2/CheckCredentials", content);
var value = await response.Content.ReadAsStringAsync();
// I need to return UserProfile
return JsonConvert.DeserializeObject<UserProfile[]>(value);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
The returned Task will be unwrapped and your caller will be given the Result of the Task, which in this case will be UserProfile[]
I'm having a problem with an async method that I implemented. The method basically makes a HttpRequest to a resource and deserializes the string if the request is successful. I wrote a test for the method, and it works. But the method does never return when I call it from a controller?
public async Task<IEnumerable<T>> Get()
{
try
{
var resourceSegmentUri = new Uri(_uri, UriKind.Relative);
var response = await _client.GetAsync(resourceSegmentUri);
if (response.IsSuccessStatusCode)
{
var submission = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<T>>(submission);
}
if (response.Content != null)
{
var message = response.Content.ReadAsStringAsync();
throw new WebException(message.Result, (WebExceptionStatus)response.StatusCode);
}
}
catch (WebException e)
{
Logger.Error("GET Request failed with status: {0}", e.Status);
throw;
}
throw new Exception();
}
Code that never returns:
public ActionResult Index()
{
var api = new Api();
var test = api.Get().Result; //Never returns
return View();
}
Test that works:
[Test]
public void GetShouldReturnIfSuccessfulRequest()
{
var api = new Api();
var submission = api.Get();
Console.WriteLine(JsonConvert.SerializeObject(submission));
Assert.NotNull(submission);
}
Does anyone know the problem?
You've got a deadlock because you're calling .Result in your controller action.
If you use async/await then you have to use asynchronous actions too.
So something like this should fix it:
public async Task<ActionResult> Index()
{
var api = new Api();
var test = await api.Get(); // Should return
}
There's a comprehensive article about this here: Using Asynchronous Methods in ASP.NET MVC 4