I'm trying to unit test my web services using Moq, and I have the following test:
[TestMethod()]
public void GetCoursesTest()
{
var response = _coursesController.GetCourses();
var contentResult = response as OkNegotiatedContentResult<List<Course>>;
Assert.AreEqual(contentResult.Content.Count(), _educationCoursesData.Count);
}
Here, GetCourses() is:
public IHttpActionResult GetCourses()
{
try
{
return Ok(_entities.Courses.ToList().Select(course => new CourseDTO
{
Id = course.Id,
CrsName = course.CrsName,
CrsHour = course.CrsHour,
CrsDay = course.CrsDay,
CrsMin = course.CrsMin,
Teacher = course.Teacher
}));
}
catch
{
return InternalServerError();
}
}
So its result is a list of obejcts. However, when I try to grab the list of these objects into contentResult via response as OkNegotiatedContentResult<List<Course>>;, contentResult is just a null. How can I read the list of objects from the OkResult into a variable that I can do Count() on?
If contentResult is null after
var contentResult = response as OkNegotiatedContentResult<List<Course>>;
the cast failed. The reason is you are using .ToList() to early. By using .Select(...) you are creating an IEnumerable and no List. Try this one:
return Ok(_entities.Courses.Select(course => new CourseDTO
{
Id = course.Id,
CrsName = course.CrsName,
CrsHour = course.CrsHour,
CrsDay = course.CrsDay,
CrsMin = course.CrsMin,
Teacher = course.Teacher
})ToList());
Alternatively change the cast to
OkNegotiatedContentResult<IEnumerable<Course>>;
The method under test is not returning OkNegotiatedContentResult<List<Course>> as the content was not enumerated. Either change the expected return type (OkNegotiatedContentResult<IEnumerable<Course>>) or update the action to enumerate the collect (.ToList()) before returning its data.
Related
I am trying to create a simple unit test to test this function that return data from a repository. When i comment out the Viewdata the test is working as expected. My question is how can i test this repository call with the Viewdata In it. Thanks For Your Help!
public async Task<GridItem> LoadItemDetailByItemIdAsync(int ItemId)
{
//calling repostiory
var result = await _itemsQueryRepository.LoadItemDetailByItemIdAsync(ItemId);
**setting the repository result to viewdata to be used
this is returning NULL expception error in my test below**
ViewData["CircuitDetails"] = result;
return result;
}
This is Unit Test that i was creating but getting an error whenever it tries to test the viewdata.
[Test]
public async Task LoadItemDetail_ReturnsData()
{
//Arrange
int ItemId = 6;
var ItemDetail = new ListItemContainer<GridItem>();
ItemDetail.Items.Add(new GridItem()
{
AccountTypeId = 1,
ItemIdStatus = 6,
ItemTypeId = 1,
ItemCostNumber = "0504",
Comments = "Send Help"
});
var tcs = new TaskCompletionSource<ListItemContainer<GridItem>>();
tcs.SetResult(ItemDetail);
_iItemQueryRepositoryMock.Setup(m => m.LoadItemDetailByItemIdAsync(It.IsAny<int>())).Returns(tcs.Task);
//Act
var itemModel = new itemsModel(_itemsQueryRepository,
_contextAccessor, _loggerItemModel);
**This returns a Null Exception error when it hit the Respository ViewData**
var actionResult = await circuitModel.LoadItemDetailByItemIdAsync(ItemId);
////Assert
Assert.NotNull(actionResult);
}
I return IActionResult with the value of an anonymous object from a controller method.
How do I get the data out again?
Here is the controller method reduced to the very problem:
[HttpPost]
public async Task<IActionResult> UploadFile(IFormFile file)
{
long size = file.Length;
return Ok(new { count = 1, size });
}
Then I get the .Value property, which is an object and make it dynamic.
But no, it is not recognised.
[Fact]
public async Task UploadFileTest()
{
// # Given.
var formFile = new Mock<IFormFile>();
var sut = new HomeController(null);
// # When.
IActionResult result = await sut.UploadFile(formFile.Object);
// # Then.
OkObjectResult okResult = (OkObjectResult)result; // Ok.
dynamic dynValue = okResult.Value; // Ok.
var count = dynValue.count; // Not ok.
}
Expected result:
count should be 1.
Actual result:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : 'object' does not contain a definition for 'count'
Try reflection:
var count = (int)okResult.Value.GetType().GetProperty("count").GetValue(okResult.Value);
If your test and original code not in the same project, If that so, you need to create AssemblyInfo.cs in your original code project to make it (original) share the dynamic object structure.
you could see more at https://sodocumentation.net/csharp/topic/4264/assemblyinfo-cs-examples#-internalsvisibleto-
It shall has code like below (Replaced the string as your test name)
[assembly: InternalsVisibleTo("MyAssembly.UnitTests")]
I am new to MS Unit Testing and Moq objects. I am trying to test my Web API 2 controller. I have given below my unit test and controller code. While stepping through the code, it doesn't even go to the GetDeliveryCodeStrategy method.
[TestMethod]
public void CreateDelivery_ShouldReturnDeliveryCode()
{
Mock<IDeliveryStrategy> deliveryStrategy = new Mock<IDeliveryStrategy>
();
Mock<IDeliveryCode> deliveryCode = new Mock<IDeliveryCode>();
var controller = new DeliveryCodeController(deliveryStrategy.Object,
deliveryCode.Object);
var controllerContext = new HttpControllerContext();
var request = new HttpRequestMessage();
request.Headers.Add("appToken", "a57ffa87-950e-40f4-b965-17788becac7d");
controllerContext.Request = request;
controller.ControllerContext = controllerContext;
var result = controller.CreateDelivery(50) as
CreatedNegotiatedContentResult<IDeliveryCode>;
Assert.IsNotNull(result);
}
public class DeliveryCodeController : ApiController
{
IDeliveryStrategy _deliveryBatch;
IDeliveryCode _deliveryCode;
//Constructor dependency injection through Autofac
public DeliveryCodeController(IDeliveryStrategy DeliveryBatch,
IDeliveryCode deliveryCode)
{
_deliveryBatch = DeliveryBatch;
_deliveryCode = deliveryCode;
}
[HttpPost]
[Route("api/DeliveryCode/{percentage}")]
public IHttpActionResult CreateDelivery(int percentage)
{
String appToken = String.Empty;
if (Request.Headers.TryGetValues("appToken", out IEnumerable<String>
headerValues))
{
appToken = headerValues.FirstOrDefault();
}
if (!String.IsNullOrEmpty(appToken)))
{
IDeliveryContext deliveryContext =
_deliveryBatch.GetDeliveryCodeStrategy(percentage);
_deliveryCode.Code = deliveryContext.Create();
return Created(Request.RequestUri.ToString(), _deliveryCode);
}
else
{
return Content(HttpStatusCode.Forbidden, new Error { message = "The App
Token is not valid." });
}
}
}
When I do the "Debug Test" and step through the code, the deliveryContext
object comes as null in the code IDeliveryContext deliveryContext =
_deliveryBatch.GetDeliveryCodeStrategy(percentage);
You have to set up the Mock to return a certain value:
IDeliveryContext deliveryContext = // ???? - whatever you want it to be.
// Could be another Mock.
// This is what the Mock will return.
Mock<IDeliveryStrategy> deliveryStrategy = new Mock<IDeliveryStrategy>();
deliveryStrategy.Setup(x => x.GetDeliveryCodeStrategy(It.IsAny<decimal>()))
.Returns(deliveryContext);
This tells the Mock that that when its GetDeliveryCodeStrategy method is called, it should return the specified IDeliveryContext. Depending on what you're trying to do, that could be another Mock. (Mocks that return mocks are undesirable, but if you're starting out I'd file that detail away and come back to it.)
I'm guessing that percentage is a decimal. It.IsAny<decimal>() means that the mock doesn't care what the value is. That's usually okay because what you're testing is what your class does with the object returned by the mock.
You need to call Setup() on mock objects for the methods that you want to use:
var deliveryStrategy = new Mock<IDeliveryStrategy>();
deliveryStrategy.Setup(x => x.GetDeliveryCodeStrategy(It.IsAny<int>))
.Returns(AMockOfDeliveryContext); //you need to mock it beforehand so you can
//use the object here
I'm trying to understand why the following unit test does not execute the callback. If I modify the code so that the UpdateWorklowInstanceState method only contains 2 parameters (Guid and IList), it works. However, something about having 3 parameters interferes.
What I mean by interferes is that the Callback doesn't appear to get executed. There's no error message. I expect to see the "Error Occurred" message but instead receive an "Element Updated" message which means the Callback did not populate the resultMessages with the NotificationMessage.
public void BusinessObjectReturnsErrorNotification_ReturnErrorMessage()
{
var workflowInstanceGuid = Guid.NewGuid();
var workflowElementModel = new WorkflowElementModel
{
ElementName = "ValidName",
WorkflowInstanceId = workflowInstanceGuid.ToString()
};
var workflowElementInstance = new WorkflowElementInstance
{
ElementName = workflowElementModel.ElementName,
FullDescription = "Full Description",
SummaryDescription = "Summary Description",
RefTime = DateTime.Now,
ElementType = "ElementType"
};
var mockWebApiBusinessObject = new Mock<IWebApiBusinessObject>();
mockWebApiBusinessObject.Setup(m => m.UpdateWorkflowInstanceState(workflowInstanceGuid, workflowElementInstance, It.IsAny<List<NotificationMessage>>()))
.Callback<Guid, WorkflowElementInstance, IList<NotificationMessage>>(
(workflowInstanceId, elementDetails, resultMessages) =>
{
resultMessages.Add(new NotificationMessage("An Error Occured!", MessageSeverity.Error));
});
var controller = new WorkflowElementController(mockWebApiBusinessObject.Object);
var result = controller.UpdateWorkflowElement(workflowElementModel);
Assert.AreEqual("An Error Occured!", result.Content.ReadAsStringAsync().Result);
}
Method under test:
[HttpPost]
[ActionName("UpdateWorkflowElement")]
public HttpResponseMessage UpdateWorkflowElement(WorkflowElementModel workflowElementModel)
{
if (!ModelState.IsValid || workflowElementModel == null)
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
var response = new HttpResponseMessage(HttpStatusCode.OK);
string responseMessage;
if (workflowElementModel.RefTime == DateTime.MinValue)
{
workflowElementModel.RefTime = DateTime.UtcNow;
}
var resultMessages = new List<NotificationMessage>();
var instanceId = new Guid();
if (string.IsNullOrWhiteSpace(workflowElementModel.WorkflowInstanceId) ||
string.IsNullOrWhiteSpace(workflowElementModel.ElementName))
{
responseMessage = "WorkflowInstanceId or ElementName are null or empty";
}
else if (!Guid.TryParse(workflowElementModel.WorkflowInstanceId, out instanceId))
{
responseMessage = "WorkflowInstanceId is not a valid Guid";
}
else
{
var element = new WorkflowElementInstance
{
ElementName = workflowElementModel.ElementName,
RefTime = workflowElementModel.RefTime,
SummaryDescription = workflowElementModel.SummaryDescription ?? "",
FullDescription = workflowElementModel.FullDescription ?? ""
};
_webApiBusinessObject.UpdateWorkflowInstanceState(instanceId, element, resultMessages);
responseMessage = "Element Updated";
}
if (NotificationMessage.HasErrors(resultMessages))
{
responseMessage = resultMessages.Find(m => m.Status == MessageSeverity.Error).Message;
}
response.Content = new StringContent(responseMessage);
return response;
}
It does not work in you case for 3 parameters because you are mixing the expression parameter types.
It.IsAny<List<NotificationMessage>>()
in the setup, as apposed to the
IList<NotificationMessage>
in the callback parameters.
That means the setup expression parameters does not match the callback expression parameters so the call back is not going to be called.
Stick with one type so either go with the List<NotificationMessage> for both
You are also creating new instances of the parameters in the method under test, which would be different instance to the ones used in the setup. That is why the call back is not working. To prove it. Use It.IsAny<>() for all the parameters and it should work
mockWebApiBusinessObject
.Setup(m => m.UpdateWorkflowInstanceState(It.IsAny<Guid>(), It.IsAny<WorkflowElementInstance>(), It.IsAny<List<NotificationMessage>>()))
.Callback<Guid, WorkflowElementInstance, List<NotificationMessage>>(
(workflowInstanceId, elementDetails, resultMessages) =>
{
resultMessages.Add(new NotificationMessage("An Error Occured!", MessageSeverity.Error));
});
Or the more generic interface
mockWebApiBusinessObject
.Setup(m => m.UpdateWorkflowInstanceState(It.IsAny<Guid>(), It.IsAny<WorkflowElementInstance>(), It.IsAny<IList<NotificationMessage>>()))
.Callback<Guid, WorkflowElementInstance, IList<NotificationMessage>>(
(workflowInstanceId, elementDetails, resultMessages) =>
{
resultMessages.Add(new NotificationMessage("An Error Occured!", MessageSeverity.Error));
});
You should also take some time and review Moq Quickstart to get a better understanding of how to use the mocking framework.
Please consider updating at minor places in your unit test.
Add before mocking IWebApiBusinessObject object:
List<NotificationMessage> messages = new List<NotificationMessage>();
Additionally, update Callback :
var mock = new Mock<IWebApiBusinessObject>();
mock.
Setup(m => m.UpdateWorkflowInstanceState(It.IsNotNull<Guid>(), It.IsNotNull<WorkflowElementInstance>(),It.IsAny<List<NotificationMessage>>() )).
Callback(() =>
{
messages.Add(new NotificationMessage("error msg", MessageSeverity.Severe));
messages.Add(new NotificationMessage("Ignore Message", MessageSeverity.Normal)); // this is optional... u can remove it if u want.
});
And need to update the source code method UpdateWorkflowElement(WorkflowElementModel model) to
UpdateWorkflowElement(WorkflowElementModel model, List<NotificationMessage> messages);
Consider changes in unit test code calling UpdateWorkflowElement to
var result = controller.UpdateWorkflowElement(workflowElementModel, messages);
If I have understood your UpdateWorkflowInstanceState() method correctly,
then you are using IWebApiBusinessObject to call UpdateWorkflowInstanceState( , , ) method.
When UpdateWorkflowInstanceState( , , ) executes during unit testing, it fires the Callback in your unit test and adds messages in list of NotificationMessage.
I'm new to unit testing in ASP.NET so please forgive my ignorance on this. I'm trying to test my controller.
This is the function in my controller which I'm testing:
public IHttpActionResult GetCustId(string name)
{
var c_id = db.Customer.Where(s => (s.c_Name == name));
if (c_id == null)
{
return null;
}
return Ok(c_id);
}
And this is my unit test code:
public void GetName_ShouldReturnCorrectId()
{
var context = new TestSContext();
context.Customers.Add(new Customer { c_ID = 1, c_Name = "jonny"});
var controller = new CustomerController(context);
var result = controller.GetCustId("Johnny") as OkNegotiatedContentResult<Customer>; //ISSUE: Result is always NULL
Assert.IsNotNull(result);
Assert.AreEqual(1, result.Content.c_ID);
}
The issue is here:
var result = controller.GetServiceId("Johnny") as OkNegotiatedContentResult<Customer>
because it is always returning NULL.
BUT... If I use just this:
var result = controller.GetCustId("Johnny");
Then the result is not null. And the first assert passes.
But I can't use it because I'm not sure how to check the second assert statement without using result.Content. I'm really not sure what are the best practices to be testing in my case.
Appreciate any help.
You are trying to find "Johnny" (with 'h') when you have put "jonny" into your mock context thus method always returns null due to your if statement
if (c_id == null)
{
return null;
}
Adding to #nizzik's answer, which is correct based on your example, to avoid simple mistakes like that you should store your values in variables and reuse them to make sure that they are as intended.
public void GetName_ShouldReturnCorrectId() {
//Arrange
var name = "Johnny";
var expectedId = 1;
var context = new TestSContext();
context.Customers.Add(new Customer { c_ID = expectedId, c_Name = name});
var controller = new CustomerController(context);
//Act
var result = controller.GetCustId(name) as OkNegotiatedContentResult<Customer>;
//Assert
Assert.IsNotNull(result);
Assert.AreEqual(expectedId, result.Content.c_ID);
}
That way you can change any one of them and the test should execute as expected.