I'm starting a new project from scratch. Currently I'm working through design of some elements and attempting to implement unit testing early on. The problem, the unit tests are failing due to what appears to be a null response from a controller action. In the Watch Window, actionResult displays "Internal error in the expression evaluator" and contentResult is null.
How do I fix this?
xunit 2.2.0.3545, Moq 4.7.63.0
Here is the unit test (xunit):
// Used in the unit tests
private List<City> cities = new List<City>
{
new City { City_Name = "Chicago", City_Code = 1 },
new City { City_Name = "New York", City_Code = 2 },
new City { City_Name = "Seattle", City_Code = 3 }
};
[Fact]
public async Task Get_AllCities_PropertiesArePopulated()
{
// Arrange
var mockService = new Mock<ICityService>();
mockService.Setup(x => x.GetCities()).ReturnsAsync(this.cities);
var controller = new CityController(mockService.Object);
// Act
IHttpActionResult actionResult = await controller.GetCities();
var contentResult = actionResult as OkNegotiatedContentResult<List<City>>;
foreach (var city in contentResult.Content)
{
// doesn't get here due to .Content being null.
}
}
Here is my controller:
public class CityController : ApiController
{
private readonly ICityService cityService;
public CityController(ICityService svc)
{
this.cityService = svc;
}
public async Task<IHttpActionResult> GetCities()
{
var cities = await this.cityService.GetCities();
if (!cities.Any())
{
return this.BadRequest();
}
return this.Ok(cities);
}
}
The service interface:
public interface ICityService
{
Task<IEnumerable<City>> GetCities();
}
GetCities return IEnumerable<City>
Task<IEnumerable<City>> GetCities();
but in the cast of the test you cast it to the wrong type using List<City>
var contentResult = actionResult as OkNegotiatedContentResult<List<City>>;
When you should be using IEnumerable<City>
var contentResult = actionResult as OkNegotiatedContentResult<IEnumerable<City>>;
Related
I have a web api as below, which is being called from my angularjs UI.
public class ValuesController : Controller
{
private static string dynamoDbTable = string.Empty;
private readonly IDynamoDbManager<MyModel> _dynamoDbManager;
public ValuesController(IOptions<Dictionary<string, string>> appSettings, IDynamoDbManager<MyModel> dynamoDbManager)
{
var vals = appSettings.Value;
dynamoDbTable = vals["dynamoDbTable"];
_dynamoDbManager = dynamoDbManager;
}
[HttpGet("api/data")]
public async Task<IActionResult> GetAllData(string status, string role)
{
List<ScanCondition> conditions = new List<ScanCondition>();
conditions.Add(new ScanCondition("status", ScanOperator.Contains, status));
conditions.Add(new ScanCondition("role", ScanOperator.Contains, role));
List<MyModel> model = new List<MyModel>();
model = await _dynamoDbManager.GetAsync(conditions);
return Ok(model);
}
[HttpPost("api/save")]
public async Task<IActionResult> SaveData([FromBody] List<MyModel> listData, string input, string name, string type)
{
List<MyModel> model = new List<MyModel>();
foreach (var data in listData)
{
//populating data here and saving
await _dynamoDbManager.SaveAsync(data);
}
return Ok();
}
}
}
Now I want to write test cases for my API endpoints. I have not written any unit test case before so need input in that. I have read basic documentation of xunit here.
Here is my sample test method:
public class ValuesControllerTests
{
private Mock<IDynamoDbManager<MyModel>> _dbManager;
private ValuesController _valueController;
public ValuesControllerTests()
{
var mockRepository = new MockRepository(MockBehavior.Loose);
_dbManager = mockRepository.Create<IDynamoDbManager<MyModel>>();
var options = new OptionsWrapper<Dictionary<string, string>>(new Dictionary<string, string>()
{
{"dynamoDbTable", nameof(MyModel) }
});
_valueController = new ValuesController(options, _dbManager.Object);
}
[Fact]
public async Task GetAllData_Test()
{
var searchResult = new List<MyModel>()
{
new MyModel(){ }
};
//Here I am trying to use MOQ but this is not working fine
_dbManager
.Setup(_ => _.GetAsync(It.IsAny<List<ScanCondition>>()))
.ReturnsAsync(searchResult);
var result = _valueController.GetAllData("new", "admin");
Assert.IsType<OkObjectResult>(result.Result);
}
}
}
Can I test my method without using moq, if so how can I do so? Because with moq I am not able to test.
Secondly above test case I am just checking if api method returns OK. What other test cases can I write for my get and save method.
I have also created a git project in case anyone wants to look at project: https://github.com/kj1981/xunit
Would appreciate if someone can help me in this.
--Added--
I also tried one test as below, but is this fine? May be my thinking is wrong, with the below test I thought it would fail but it does not.
[Fact]
public async Task GetAllData_Test()
{
var searchResult = new List<MyModel>()
{
new MyModel(){ Id ="1", Name = "Adam", Role = "User", Status ="old" },
new MyModel(){ Id ="2", Name = "Gary", Role = "Admin", Status ="new" }
};
_dbManager
.Setup(_ => _.GetAsync(It.IsAny<List<ScanCondition>>()))
.ReturnsAsync(searchResult);
var okResult = _valueController.GetAllData("fail", "none").Result as OkObjectResult; ;
var items = Assert.IsType<List<MyModel>>(okResult.Value);
Assert.Equal(2, items.Count);
}
Anyone with inputs ?
I am using .NET Core with xUnit/Moq to create unit tests. I would like to create a unit test for the following API call:
[HttpGet("{zip}")]
public IActionResult Get(int zip)
{
//debugging here shows the repository has the object
//but the result is always null
Location result = repository[zip];
if(result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
}
The unit test I have (that's failing) is:
[Fact]
public void Api_Returns_Json_Object()
{
//Arrange
Mock<IRepository> mockRepo = new Mock<IRepository>();
mockRepo.Setup(m => m.Locations).Returns(new Location[]
{
new Location
{
zip = 88012,
type = "STANDARD",
state = "NM"
}
});
//Arrange
ApiController controller = new ApiController(mockRepo.Object);
// Act
var response = controller.Get(88012);
// Assert
Assert.True(response.Equals(HttpStatusCode.OK));
}
When I debug, the repository shows the correct Location object, but the result is always null, returning a NotFound() status code.
If I test the response with PostMan it works correctly.
Here are the relevant IRepository members:
IEnumerable<Location> Locations { get; }
Location this[int zip] { get; }
Based on what is accessed within the method under test, the wrong member was set up when arranging the test
[Fact]
public void Api_Returns_Json_Object() {
//Arrange
int zip = 88012;
var location = new Location
{
zip = zip,
type = "STANDARD",
state = "NM"
};
Mock<IRepository> mockRepo = new Mock<IRepository>();
mockRepo.Setup(m => m[zip]).Returns(location);
var controller = new ApiController(mockRepo.Object);
// Act
var response = controller.Get(zip);
var okResult = response as OkObjectResult;
// Assert
Assert.NotNull(okResult);
Assert.Equal(location, okResult.Value);
}
I'm trying to unit test an action in an MVC controller but the mocked object is returning null.
I have a ClientProxy interface
public interface IClientProxy
{
Task<T> Get<T>(string uri);
}
With the following concrete implementation
public async Task<T> Get<T>(string uri)
{
using (HttpClient httpClient = new HttpClient())
{
ConfigureClient(httpClient);
var response = await httpClient.GetAsync(uri);
response.EnsureSuccessStatusCode();
return await Task.Run(() => GetResultFromResponse<T>(response));
}
}
This is my action
public async Task<ActionResult> Index()
{
var categories = await _proxy.Get<PagedResults<Category>>("/category/get");
return View("index", null, JsonConvert.SerializeObject(categories));
}
And this is the unit test class
public class CategoryControllerTests
{
private readonly CategoryController _controller;
private readonly Mock<IClientProxy> _mockProxy;
public CategoryControllerTests()
{
_mockProxy = new Mock<IClientProxy>();
_controller = new CategoryController(_mockProxy.Object);
}
[Fact]
public async Task IndexPageRenders()
{
// Arrange
var fakeResult = new PagedResults<Category>
{
Paging = new Paging
{
Page = 1,
PageSize = 10,
TotalRecords = 2
},
Results = new List<Category>
{
new Category { CategoryId = 1, Name = "Category One" },
new Category { CategoryId = 2, Name = "Category Two" }
}
};
var result = JsonConvert.SerializeObject(fakeResult);
_mockProxy.Setup(p => p.Get<PagedResults<Category>>(It.IsAny<string>())).ReturnsAsync(fakeResult);
// Act
var action = await _controller.Index() as ViewResult;
// Assert
_mockProxy.Verify(p => p.Get<PagedResults<Category>>(It.IsAny<string>()), Times.Exactly(1));
}
}
Currently the mocked object setup for the get method is not getting hit, and categories are null (the Verify is failing, implying that the mocked method is never called). However, if I remove the await keyword from the action then the categories are returned. I'd rather not remove the await just to pass the test as it's there for a good reason, so any help would be appreciated.
Thanks in advance.
Currently the mocked object setup for the get method is not getting hit, and categories are null (the Verify is failing, implying that the mocked method is never called).
That would mean that the mocked object was not passed as a dependency to the controller being tested and as such is not being invoked when the test is being exercised.
Here is a Minimal, Complete, and Verifiable example that demonstrates how one would setup and exercise a test in such a scenario.
The follow were used
public class MyModel {
public int MyIntProperty { get; set; }
public string MyStringProperty { get; set; }
}
public interface IClientProxy {
Task<T> Get<T>(string uri);
}
public class MyController : Controller {
IClientProxy _proxy;
public MyController(IClientProxy _proxy) {
this._proxy = _proxy;
}
public async Task<ActionResult> Index() {
var categories = await _proxy.Get<MyModel>("/category/get");
return View(categories);
}
}
And following test was used with MSTest, Moq and Fluent Assertions where is flowed to completion and passed.
[TestClass]
public class MyController_Should {
[TestMethod]
public async Task _Render_Index_Page() {
// Arrange
var fakeResult = new MyModel {
MyIntProperty = 0,
MyStringProperty = "Hello World."
};
var _mockProxy = new Mock<IClientProxy>();
_mockProxy
.Setup(_ => _.Get<MyModel>(It.IsAny<string>()))
.ReturnsAsync(fakeResult);
var _controller = new MyController(_mockProxy.Object);
// Act
var action = await _controller.Index() as ViewResult;
// Assert
_mockProxy.Verify(_ => _.Get<MyModel>(It.IsAny<string>()), Times.Exactly(1));
action.Should().NotBeNull();
var model = action.Model;
model.Should().NotBeNull()
.And.BeOfType<MyModel>()
.And.Be(fakeResult);
}
}
I would suggest reviewing this example and then comparing it to how you designed your test to see if you can identify what may be causing your problem.
I am writing a unit test to test an MVC controller method (CRUD operation). The controller method accepts the usernameIdentity from session for auditing purposes.
I am trying the following to setup the ShimHttpSessionStateBase:
[TestMethod()]
public void CategoryCreateTest()
{
using (ShimsContext.Create())
{
var controller = new WebcamsController();
var category = new DataModel.Webcams.Category() { Name = "TestCategory" };
var currentSessionWrapper = new HttpSessionStateWrapper(HttpContext.Current.Session);
var session = new System.Web.Fakes.ShimHttpSessionStateBase(currentSessionWrapper);
session.Instance.Add("usernameIdentity", "DOMAIN\\USERNAME");
controller.ControllerContext = new ControllerContext(controller);
var result = controller.CategoryCreate(category);
Assert.IsInstanceOfType(result, typeof(PartialViewResult));
}
}
I am not sure how to appropriately create the controllerContext so that the Session will be read in the controller method.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CategoryCreate([Bind(Include = "ID,Name")] Category category)
{
try
{
if (category != null
&& Validation.ValidNonNullableString(Session["usernameIdentity"].ToString(), 1, 60) && ModelState.IsValid)
{
businessLayer.SetCategories(category, "Add", AuthenticationHandler.GetHostInternetProtocolAddress, Session["usernameIdentity"].ToString(), AuthenticationHandler.GetUserInternetProtocolAddress);
return PartialView("CategoryList", businessLayer.GetCategoriesByCategory(0, AuthenticationHandler.GetHostInternetProtocolAddress));
}
return jsonResultHandler.JsonResultMessage("Failure", Properties.Resources.GlobalAttributeCreateFailed);
}
catch
{
throw;
}
}
Here's a sample of one of my unit test classes (pared down to the basics). In the controller, when the Index() action method is invoked, a call to GetByID(1234) always results in a newed up instance of a Ticket object. The object exists, but all of its properties are null, even though I've set them in my fake object. Any ideas as to why?
I'm using Moq.
Unit test
[TestClass]
public class TicketControllerTests : ControllerTestBase
{
protected Mock<ITicketRepository> MockTicketRepository = new Mock<ITicketRepository>();
[TestMethod]
public void IndexActionModelIsTypeOfTicketModel()
{
//ARRANGE
Mock<HttpContextBase> context = FakeHttpContext();
context.Setup(ctx => ctx.Session[SessionKeys.TokenData.ToString()]).Returns(Constants.TOKENDATA_SUBMITTER);
MockTicketRepository.Setup(x => x.GetById(It.IsAny<int>())).Returns(Constants.CLIENT_TICKET);
//ACT
var result = GetController(context.Object).Index(Constants.TICKET_ID);
var model = ((ViewResult)result).Model;
//ASSERT
Assert.IsInstanceOfType(model, typeof(TicketModel), "ViewModel should have been an instance of TicketModel.");
}
private TicketController GetController(HttpContextBase context)
{
var controller = new TicketController(MockTicketRepository.Object);
controller.ControllerContext = GetControllerContext(context, controller);
return controller;
}
}
Constants.CLIENT_TICKET
public static Ticket CLIENT_TICKET
{
get
{
var ticket = new Ticket
{
CategoryID = 1,
CreatedByUserId = 4
};
ticket.Clients.Add(new Client { ShortName = "Test Client 1"});
ticket.Clients.Add(new Client { ShortName = "Test Client 2" });
ticket.User = new User {FirstName = "First", LastName = "Last"};
return ticket;
}
}
Controller
private readonly ITicketRepository _ticketRepository;
public TicketController(ITicketRepository ticketRepository)
{
_ticketRepository = ticketRepository;
}
public ActionResult Index(int id)
{
var ticket = _ticketRepository.GetById(id);
// etc...
}
Could you show the controller code under test? It could be related to how you have set up the mocked context but it's hard to tell without seeing the controller code.
Also, if you add MockBehavior.Strict when you create the mock, it will bomb out if the invocation doesn't have a corresponding expectation:
protected Mock<ITicketRepository> MockTicketRepository = new Mock<ITicketRepository>(MockBehavior.Strict);
UPDATE
I've tried to strip everything back so that the test is as simple as possible to try and isolate the issue. Here's what I have come up with:
[TestClass]
public class TicketControllerTests : ControllerTestBase
{
protected Mock<ITicketRepository> MockTicketRepository;
[TestMethod]
public void IndexActionModelIsTypeOfTicketModel()
{
//ARRANGE
MockTicketRepository = new Mock<ITicketRepository>(MockBehavior.Strict);
MockTicketRepository.Setup(x => x.GetById(Constants.TICKET_ID)).Returns(Constants.CLIENT_TICKET);
var controller = new TicketController(MockTicketRepository.Object);
//ACT - try to keep ACT as lean as possible, ideally just the method call you're testing
var result = controller.Index(Constants.TICKET_ID);
//ASSERT
var model = ((ViewResult)result).ViewData.Model;
Assert.That(model, Is.InstanceOfType<TicketModel>(), "ViewModel should have been an instance of TicketModel.")
}
}