How to mock a post request? - c#

Controller:
private IBeer _beerService;
public BeerController(IBeer beerService)
{
_beerService = beerService;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody]Beer model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var beer = await _beerService.Add(model);
return Ok(CreatedAtAction("Add Item", new { beer.id }, beer));
}
Model:
public class Beer
{
public int id { get; set; }
public string name { get; set; }
public int cost { get; set; }
}
Interface :
public interface IBeer
{
Task<Beer> Add(Beer beer);
}
Test:
[Fact]
public void TestPostWithMock()
{
// Arrange
var serviceMock = new Mock<IBeer>();
List<Beer> expected = new List<Beer>()
{
new Beer{id=2, beer="Kingfisher", cost=170 },
};
serviceMock.Setup(x => x.Add(expected)).Returns(() => Task.FromResult(beer));
var controller = new BeerController(serviceMock.Object);
// Act
var result = controller.Get(2);
// Assert
var okResult = result.Should().BeOfType<OkObjectResult>().Subject;
var actual = okResult.Value.Should().BeAssignableTo<IEnumerable<Beer>>().Subject;
Assert.Equal(expected, actual);
}
I'm trying to mock the post request but when I try to mock it here:
x => x.Add(expected)
It gives me an error - Generic.list cannot be converted to Controller.Beer. What should be done ?

As error description mentions, you send list of beer (List<Beer>) to your Add() method while setuping your service. You should instead send Beer class instance (like in your interface) as parameter like shown below.
// Update your request object without List
var expected = new Beer()
{
id=2,
beer="Kingfisher",
cost=170
};
And also, update your Setup function as below.
serviceMock.Setup(x => x.Add(expected)).Returns(() => Task.FromResult(expected));
Lastly, you need to get your result from controller.Post() method as in the controller, not from controller.Get(2). Hope this helps you.

Related

How to Unit test 2 consecutive calls to the same method with different parameter types

Working with C# .NetFrameWork 4.7, Moq 1.4
I'm testing a method that makes 2 interface calls. The interface method takes in 1 parameter, and that parameter is an interface as well. Having a hard time testing this, Moq is throwing casting errors.
2 consecutive calls with the following signiture:
InterfaceUnderTest.MethodUnderTest(IEnumerable<IParamInterface>)
The complete code example can be found here on github: https://github.com/JayZhang727/UnitTestingInterfaceParams
Following is the basic structure:
interfaces and implementations being imported that I can not change:
public interface IWorkInterface
{
string DoWork(IEnumerable<IParamInterface> para);
}
public interface IParamInterface
{
IParamDataInterface GetParamData();
}
public interface IParamDataInterface
{
string Id { get; set; }
}
public class ParamClass<T> : IParamInterface where T : IParamDataInterface
{
public T ParamData { get; set; }
public ParamClass()
{
}
public ParamClass(T para)
{
this.ParamData = para;
}
public IParamDataInterface GetParamData()
{
return ParamData;
}
}
public class ParamClassA : IParamDataInterface
{
public ParamClassA()
{
this.Id = "Id";
this.ParamClassAVar = 123;
}
public string Id { get; set; }
public int ParamClassAVar { get; set; }
}
public class ParamClassB : IParamDataInterface
{
public ParamClassB()
{
this.Id = "Id";
this.ParamClassBVar = "not 123";
}
public string Id { get; set; }
public string ParamClassBVar { get; set; }
}
Here is the class under test:
public ClassUnderTest(IWorkInterface workInt)
{
this.WorkClient = workInt;
}
public IWorkInterface WorkClient { get; set; }
public string MethodUnderTest()
{
var result = string.Empty;
//Class A is an implementation of IParamDataInterface
var a = new ParamClassA();
var theParamA = new ParamClass<ParamClassA>(a);
var listA = new List<IParamInterface>();
listA.Add(theParamA);
result = WorkClient.DoWork(listA);
//Class B is also an implementation of IParamDataInterface
var b = new ParamClassB();
var theParamB = new ParamClass<ParamClassB>(b);
var listB = new List<IParamInterface>();
listB.Add(theParamB);
result = result + WorkClient.DoWork(listB);
return result;
}
Here is the test calls that I tried:
private Mock<IWorkInterface> mockClient;
[TestMethod()]
public void DoWorkTest()
{
mockClient = new Mock<IWorkInterface>(MockBehavior.Strict);
var target = new ClassUnderTest(mockClient.Object);
var mockSquence = new MockSequence();
mockClient.InSequence(mockSquence).Setup(ec => ec.DoWork(It.Is<List<IParamInterface>>(el => ((ParamClassA)((ParamClass<ParamClassA>)el[0]).GetParamData()).ParamClassAVar == 123))).Returns("123");
mockClient.InSequence(mockSquence).Setup(ec => ec.DoWork(It.Is<List<IParamInterface>>(el => ((ParamClassB)((ParamClass<ParamClassB>)el[0]).GetParamData()).ParamClassBVar == "not 123"))).Returns("not 123");
//act
target.MethodUnderTest();
//assert
mockClient.VerifyAll();
}
I get errors about can not cast from Class A to Class B:
Message: Test method ClassUnderTesting.UnitTests.ClassUnderTestingTests.DoWorkTest threw exception:
System.InvalidCastException: Unable to cast object of type 'ClassUnderTesting.ParamClass`1[ClassUnderTesting.ParamClassA]' to type 'ClassUnderTesting.ParamClass`1[ClassUnderTesting.ParamClassB]'.
It seems that Moq is not doing it in sequence and for some reason, the second setup is trumping the first setup and sequence doesn't seem to help. Anyone know how I should setup and test these 2 calls?
I would do this without a sequence. Just setup your expectations so that when DoWork is called with ParamClassA it returns "123", and when it's called with ParamClassB it returns "not 123".
Note that the first expectation will not crash if it is not given ParamClassA: it simply won't match (and likewise the second and ParamClassB).
mockClient
.Setup(ec => ec.DoWork(
It.Is<List<IParamInterface>>(el => el[0] is ParamClass<ParamClassA> && ((ParamClass<ParamClassA>)el[0]).ParamData.ParamClassAVar == 123)
))
.Returns("123");
mockClient
.Setup(ec => ec.DoWork(
It.Is<List<IParamInterface>>(el => el[0] is ParamClass<ParamClassB> && ((ParamClass<ParamClassB>)el[0]).ParamData.ParamClassBVar == "not 123")
))
.Returns("not 123");

Client.PostAsync (with Json) Gets 400 Bad Request

I am trying to create an Integration Test for my .Net Core Web Api
But I am always getting a 400 Bad Request response. I am sharing details below
Here is my Controller method
public IActionResult UpdateProductById([FromBody]int id, string description)
{
var result = ProductService.UpdateProductById(id, description);
if (result.Exception == null)
return Ok(result);
else
return BadRequest(result.Exception.Message);
}
Here is my test class (which tries to post)
[Fact]
public async Task UpdateProductById_Test_WithProduct()
{
var product = new
{
id = 1,
description = "foo"
};
var productObj= JsonConvert.SerializeObject(product);
var buffer = System.Text.Encoding.UTF8.GetBytes(productObj);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var result = await _tester.Client.PostAsync("/api/1.0/UpdateProductById", byteContent);
result.StatusCode.Should().Be(HttpStatusCode.OK);
}
The test is sending all the content in the body of the request, yet the action is only binding the id. Most likely the description is null and causing an issue with the update.
Create a model to hold the data
public class ProductModel {
public int id { get; set; }
public string description { get; set; }
}
Refactor action to get the content from the body of the request
[HttpPost]
public IActionResult UpdateProductById([FromBody]ProductModel model) {
if(ModelState.IsValid) {
var result = ProductService.UpdateProductById(model.id, model.description);
if (result.Exception == null)
return Ok(result);
else
return BadRequest(result.Exception.Message);
}
return BadRequest(ModelState);
}

Why does my web API not return JSON?

I made a very simple web API controller to reproduce an error in a more complex one, this controller only has the minimum amount of code to reproduce the error.
The controller code is as follows
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
namespace NWCloudBorgEmployee.Controllers
{
public class WmsInfo
{
public string StoreName { get; set; }
public string ItemName { get; set; }
public string ItemQty { get; set; }
}
public class WmsInformation
{
public List<WmsInfo> WmsInfos { get; set; }
}
[Produces("application/json")]
[Route("api/WmsInfo")]
public class WmsInfoController : Controller
{
// GET: api/WmsInfo
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "You need to send in a barcode as ID to get the correct return data" };
}
// GET: api/WmsInfo/5
[HttpGet("{id}", Name = "Get")]
public string Get(int id)
{
var test = new WmsInformation();
test.WmsInfos = new List<WmsInfo>
{
new WmsInfo { StoreName = "SE001", ItemName = "Item1" , ItemQty = "10"},
new WmsInfo { StoreName = "SE002", ItemName = "Item2" , ItemQty = "115"}
};
return test.ToString();
}
}
}
When I call the API I get a string as below instead of the JSON data
"NWCloudBorgEmployee.Controllers.WmsInformation"
Why is it not returning the JSON?
Because you're calling .ToString() on an object.
Change your method signature to this:
public IActionResult Get(int id)
and then simply:
return Ok(test);
Note: You can change the signature to return WmsInformation, but using action results lets you return error codes easily, too.
I recommended to use JsonResult instead of string
public JsonResult Get(int id)
{
var test = new WmsInformation();
test.WmsInfos = new List<WmsInfo>
{
new WmsInfo { StoreName = "SE001", ItemName = "Item1" , ItemQty = "10"},
new WmsInfo { StoreName = "SE002", ItemName = "Item2" , ItemQty = "115"}
};
return new JsonResult(test);
}
I hope you find it useful
That is valid JSON. It’s a string, just like your return type.
I imagine you’re trying to serialize the object, rather that use ToString, which will return the type name by default.
You could just return the object instead of using ToString, and adjust your method signature to account for the change.
This will start the content negotiation process and end up serializing your object to (probably) JSON.
public WmsInformation Get(int is)
{
var test = new WmsInformation
{
WmsInfos = ...
}
return test;
}

Add value to list

I am calling a API method that is returning some question and answer as List. I need show this list in View, not sure how to add value to the faq list. As I am sending this List that is part of Model to the View to show in on the screen.
Inside the foreach loop is where I have to add value of web api to the faq List.
This is my method which is returning the Model.
[HttpGet]
public async Task<ActionResult> ShowContact(int loanId)
{
HelpCenterViewModel helpCenterViewModel = new HelpCenterViewModel();
helpCenterViewModel.ContactInfo.loanId = loanId;
string json = string.Empty;
List<Faq> FaqObject = null;
var responseApi = await httpClient.GetAsync(string.Format("{0}/{1}",
CommonApiBaseUrlValue, "faqs"));
if (responseApi.IsSuccessStatusCode)
{
json = responseApi.Content.ReadAsStringAsync().Result;
FaqObject = new JavaScriptSerializer().Deserialize<List<Faq>>(json);
}
var response = new
{
success = FaqObject != null,
data = FaqObject
};
foreach (var faqitem in response.data)
{
//This is where I dont know how to add to faq list.
//helpCenterViewModel.Faq.Answer = faqitem.Answer;
//helpCenterViewModel.Faq.Category = faqitem.Category;
}
return View(helpCenterViewModel);
}
This is the Model that I am retunign it to view:
public class HelpCenterViewModel
{
public List<Faq> Faq { get; set; }
public ContactUsInfo ContactInfo { get; set; }
public HelpCenterViewModel()
{
this.Faq = new List<Faq>();
this.ContactInfo = new ContactUsInfo();
}
}
and this is the faq class:
public class Faq
{
public int Id { get; set; }
public string Category { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
}
and this is my view:
#model IEnum erable<Carfinance.Loans.Web.ViewModels.HelpCenterViewModel>
#foreach (var item in Model)
{
<li>#Html.DisplayFor(faq => item.Faq)</li>
}
But It gave me this error.
The 'DelegatingHandler' list is invalid because the property 'InnerHandler' of 'CorsMessageHandler' is not null.
Parameter name: handlers
You need to create a new object for each of your items and add them to your list. This can be done in various ways, depending on how verbose you want your implementation to be:
foreach (var faqitem in response.data)
{
var faq = new Faq();
faq.Answer = faqitem.Answer;
faq.Category = faqitem.Category;
helpCenterViewModel.Faq.Add(faq);
}
OR
foreach (var faqitem in response.data)
helpCenterViewModel.Faq.Add(new Faq()
{
Answer = faqitem.Answer;
Category = faqitem.Category;
});
OR
helpCenterViewModel.Faq = response.data.Select(x => new Faq {
Answer = x.Answer,
Category = x.Category
}).ToList();
Cleaned up the code some, but you already have a List<Faq>, so just assign it to your model.
[HttpGet]
public async Task<ActionResult> ShowContact(int loanId)
{
string json = string.Empty;
List<Faq> FaqObject = null; // Should probably be new List<Faq>
var responseApi = await httpClient.GetAsync(string.Format("{0}/{1}", CommonApiBaseUrlValue, "faqs"));
if (responseApi.IsSuccessStatusCode)
{
json = responseApi.Content.ReadAsStringAsync().Result;
FaqObject = new JavaScriptSerializer().Deserialize<List<Faq>>(json);
}
var response = new
{
success = FaqObject != null,
data = FaqObject
};
return View(new HelpCenterViewModel
{
ContactInfo=new ContactInfo {loanId},
Faq=FaqObject
});
}
Not sure what you were doing with the response variable, so I just left it there, but it appears to do nothing useful and could be removed as well. Then you'd have this:
[HttpGet]
public async Task<ActionResult> ShowContact(int loanId)
{
var responseApi = await httpClient.GetAsync(string.Format("{0}/{1}", CommonApiBaseUrlValue, "faqs"));
if (responseApi.IsSuccessStatusCode)
{
return View(new HelpCenterViewModel
{
ContactInfo=new ContactInfo {loanId},
Faq=new JavaScriptSerializer().Deserialize<List<Faq>>(
responseApi.Content.ReadAsStringAsync().Result)
});
}
return View(new HelpCenterViewModel
{
ContactInfo=new ContactInfo {loanId},
Faq=new List<Faq>()
});
}
well, the Faq property on HelpCenterViewModel is really a List<Faq> (kind of misleading naming you have there), so use the Add method:
foreach (var faqitem in response.data)
{
var faq = new Faq();
faq.Answer = faqitem.Answer;
faq.Category = faqitem.Category;
helpCenterViewModel.Faq.Add(faq);
//^ this Faq is a List
}
You should pluralize your List<Faq>'s name to Faqs to prevent confusing yourself.

Cannot Convert List to Json

I have the following action method:
public class MyDeviceController : ApiController
{
// GET api/<controller>
public JsonResult Get()
{
int userId = -1;
List<String> myDevices1 = new List<String>();
myDevices1.Add("1");
>> return Json(myDevices1); << Error here
}
}
The return is underlined red with the following error:
cannot implicitly convert type (JsonResult to List<string>)
I am using Asp.net Web Api. I think its getting confused between the using System.web.http and System.mvc.http
h
your system is confusing between
System.Web.Http.Results.JsonResult<List<string>>
and System.Web.Mvc.JsonResult
try specifiyng the full name which is System.Web.Http.Results.JsonResult>
public System.Web.Http.Results.JsonResult<List<string>> Get()
{
int userId = -1;
List<String> myDevices1 = new List<String>();
myDevices1.Add("1");
return Json(myDevices1);
}
another and preferred approach would be
public HttpResponseMessage Get()
{
int userId = -1;
List<String> myDevices1 = new List<String>();
myDevices1.Add("1");
return Request.CreateResponse(myDevices1);
}
in the latter teh asp.net web api would automatically negotiate between the formats accepted by the client which is specified iin the Accepts header and would send XML or JSON appropriately
My web api project in defined class City JsonResult List Convert by and return.
public class City
{
public int Id { get; set; }
public string CityName{ get; set; }
}
static List<City> _City = InitCitys();
private static List<City> Citys()
{
var returnList = new List<City>();
returnList.Add(new City{ Id = 0, CityName= "Sinop" });
returnList.Add(new City{ Id = 1, CityName= "Ayancık" });
returnList.Add(new City{ Id = 2, CityName= "İstanbul" });
return returnList;
}
// GET api/values
public JsonResult<City> Get(int Id)
{
var cityJsonResult = _City.Where(x => x.Id == Id).SingleOrDefault();
return Json(cityJsonResult);
}
As you are using web api:
You can leave your code this way:
public class MyDeviceController : ApiController
{
// GET api/<controller>
public List<string> Get()
{
int userId = -1;
List<String> myDevices1 = new List<String>();
myDevices1.Add("1");
return myDevices;
}
}
By default it will return XML but
adding this line of code in WebApiConfig.cs, will return json by default:
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
See more about WEB API media Formatters
There is also another way of doing things(not the best approach):
using Newtonsoft.Json;
public HttpResponseMessage get(){
List<String> myDevices1 = new List<String>();
myDevices1.Add("1");
JsonConvert.SerializeObject(myDevices1);
return Request.CreateResponse(HttpStatusCode.OK, myDevices1);;
}

Categories