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 ?
Related
New to Unit Testing web api.
I am writing a Unit test to Test a controller and I have to mock Iconfiguration. The appsettings,json has a section called "AppSettings", I'm trying to mock it.
Also, the mock.setup returns null value in the controller causing it to fail.
Here is my controller:
private readonly ILogger _logger;
private readonly IConfiguration _configuration;
private readonly ICarPairingTable PairingTable;
private readonly ICarDealerSettingsTable DealerSettingsTable;
static AppSettings appSettings = null;
public CarController(IConfiguration configuration, ICarPairingTable carPairingTable, ICarDealerSettingsTable settingsTable)
{
_configuration = configuration;
appSettings = configuration.Get<AppSettingsModel>().AppSettings;
PairingTable = carPairingTable;
DealerSettingsTable = settingsTable;
}
[HttpGet]
public ActionResult Get(string id){
string DealerId ="";
DealerId = PairingTable.GetDealerId(id).Result;
if (string.IsNullOrEmpty(DealerId))
{
result = new ReturnResult
{
status = "Fail",
data = "ID is invalid"
};
return NotFound(result);
}
SettingsInfo info = DealerSettingsTable.GetSettingsInfo(DealerId).Result;
if (info == null)
{
result = new ReturnResult
{
status = "Fail",
data = "Not Found"
};
return NotFound(result);
}
result = new ReturnResult
{
status = "Success",
data = info
};
return Ok(result);
}
Here is my Unit Test:
[Fact]
public void Test1()
{
var mockConfig = new Mock<IConfiguration>();
var configurationSection = new Mock<IConfigurationSection>();
configurationSection.Setup(a => a.Value).Returns("testvalue");
mockConfig.Setup(a => a.GetSection("AppSettings")).Returns(configurationSection.Object);
var mock1 = new Mock<ICarPairingTable>();
mock1.Setup(p => p.GetDealerId("456")).ReturnsAsync("123");
var mock2 = new Mock<ICarDealerSettingsTable>();
SettingsInfo mockSettings = new SettingsInfo()
{
DealerId = "123",
Name="Dealer1"
};
mock2.Setup(p => p.GetSettingsInfo("123")).ReturnsAsync(()=>mockSettings);
CarController controller = new CarController(mockConfig.Object, mock1.Object, mock2.Object);
var result = controller.Get("456");
//Dont know what to assert
}
Wrote unit test, but not sure if my approach is correct, Help will be appreciated.
This is more of a design issue wrapped in an XY problem.
Really should not be injecting IConfiguration. Based on how the configuration is using by the controller what you should have done was register the settings with the service collection in startup
Startup.ConfigureServices
//...
AppSettings appSettings = Configuration.Get<AppSettingsModel>().AppSettings;
services.AddSingleton(appSettings);
//...
and explicitly inject the settings into the controller
//...
private readonly AppSettings appSettings = null;
public CarController(AppSettings appSettings , ICarPairingTable carPairingTable, ICarDealerSettingsTable settingsTable) {
this.appSettings = appSettings;
PairingTable = carPairingTable;
DealerSettingsTable = settingsTable;
}
//...
So now when unit testing the controller in isolation, you can initialize an instance of the desired class and provide when exercising the unit test.
Reference Explicit Dependencies Principle
You also appear to be mixing async-await and blocking calls like .Result.
I sugest you make the action async all the way
[HttpGet]
public async Task<ActionResult> Get(string id){
string DealerId = await PairingTable.GetDealerId(id);
if (string.IsNullOrEmpty(DealerId)) {
var result = new ReturnResult {
status = "Fail",
data = "ID is invalid"
};
return NotFound(result);
}
SettingsInfo info = await DealerSettingsTable.GetSettingsInfo(DealerId);
if (info == null) {
var result = new ReturnResult {
status = "Fail",
data = "Not Found"
};
return NotFound(result);
}
var result = new ReturnResult {
status = "Success",
data = info
};
return Ok(result);
}
Reference Async/Await - Best Practices in Asynchronous Programming
That way the unit test can finally be arranged correctly to verify the expected behavior
[Fact]
public async Task Should_Return_Ok_ReturnRsult() {
//Arrange
var id = "456";
var dealerId = "123";
SettingsInfo expected = new SettingsInfo() {
DealerId = dealerId,
Name="Dealer1"
};
var pairingMock = new Mock<ICarPairingTable>();
pairingMock.Setup(p => p.GetDealerId(id)).ReturnsAsync(dealerId);
var dealerSettingsMock = new Mock<ICarDealerSettingsTable>();
dealerSettingsMock.Setup(p => p.GetSettingsInfo(dealerId)).ReturnsAsync(() => expected);
CarController controller = new CarController(new AppSettings(), pairingMock.Object, dealerSettingsMock.Object);
//Act
var actionResult = await controller.Get(id);
var actual = actionResult as OkObjectResult;
//Assert (using FluentAssertions)
actual.Should().NotBeNull();
actual.Value.Should().BeOfType<ReturnResult>();
var actualResult = actual.Value as ReturnResult;
actualResult.data.Should().BeEquivalentTo(expected);
}
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'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>>;
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.")
}
}