I want to write Unit test cases for following code
HomeController.cs
[HttpPost]
[ActionName("CreateDemo")]
public async Task<IHttpActionResult> CreateDemo([FromBody] MyRequest request)
{
if (request == null)
{
return BadRequest("request can not be null");
}
if (request.MyID == Guid.Empty)
{
return BadRequest("MyID must be provided");
}
}
I tried like following which is not correct way i guess so
[TestMethod]
public async Task NullCheck()
{
try
{
var controller = new HomeController();
var resposne = await controller.CreateDemo(null);
Assert.AreEqual(); // not sure what to put here
}
catch (HttpResponseException ex) //catch is not hit
{
Assert.IsTrue(
ex.Message.Contains("request can not be null"));
}
}
Each unit test shall test one requirement or concern. Your method implements two requirements:
1) If request is null, return BadRequestErrorMessageResult object with predefined error message.
2) If request's MyID property is empty GUID, return BadRequestErrorMessageResult object with another predefined error message.
This means we should have two unit tests:
[Test]
public async Task CreateDemo_returns_BadRequestErrorMessageResult_when_request_is_null()
{
// Arrange
var controller = new HomeController();
// Act
var response = await controller.CreateDemo(null);
// Assert
Assert.IsInstanceOf<BadRequestErrorMessageResult>(response);
Assert.AreEqual("request can not be null", response.Message);
}
[Test]
public async Task CreateDemo_returns_BadRequestErrorMessageResult_when_request_ID_is_empty_GUID()
{
// Arrange
var controller = new HomeController();
var request = new MyRequest(Guid.Empty);
// Act
var response = await controller.CreateDemo(request);
// Assert
Assert.IsInstanceOf<BadRequestErrorMessageResult>(response);
Assert.AreEqual("MyID must be provided", response.Message);
}
You can go even further and split each of these tests into two where one would test that return object is of the expected type and another that validates that returned object state is as expected (e.g. Message string is as expected). This way you would have a single assert per test.
Side notes:
You tagged this question with nunit tag so I provided the code which uses that framework. In your example though, you use [TestMethod] attribute which comes from Microsoft unit testing framework. If you want to use that framework you'd have to make some changes e.g. replace Assert.IsInstanceOf with Assert.IsInstanceOfType.
I assumed that GUID is passed to MyRequest via its constructor which assigns it to MyID.
I am not coming from web world but I found that BadRequest method has an overload which returns BadRequestErrorMessageResult if string is passed as its argument.
Related
I'm trying to learn Unit testing in .NET 6 by testing a controller function GetProduct. The problem is I get null returned in the variable var product = await _productController.GetProduct(productId);. As you can see in the picture below, the Result is ok but the Value, where the ServiceResponse<Product> was suppose to be is null.
Here is the controller function:
public class ProductController : ControllerBase
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
[HttpGet("{productId}")]
public async Task<ActionResult<ServiceResponse<Product>>> GetProduct(int productId)
{
var result = await _productService.GetProductAsync(productId);
return Ok(result);
}
}
Here is the Test:
public class ProductControllerTest
{
private readonly ProductController _productController;
private readonly Mock<IProductService> _productService = new Mock<IProductService>();
public ProductControllerTest()
{
_productController = new ProductController(_productService.Object);
}
[Test]
public async Task GetProducts_ReturnsProduct_IfProductExists()
{
//Arange
var productId = 1;
var prodData = new Product
{
Id = productId,
Title = "null"
};
var prductResponse = new ServiceResponse<Product>
{
Data = prodData,
Success = true ,
Message = ""
};
_productService.Setup(x => x.GetProductAsync(productId)).ReturnsAsync(prductResponse);
//Act
var product = await _productController.GetProduct(productId);
//Assert
Assert.That(product?.Value?.Data?.Id, Is.EqualTo(productId));
}
}
This behavior is observed due to overloaded operators on ActionResult<T> class.
Since the method, OK(value) returns an instance of OKObjectResult and the return type of controller method is of type ActionResult<ServiceResponse<Product>> the returned OKObjectResult instance is wrapped in an instance of ActionResult<T> and is exposed by the Result property. Hence typecasting the Result property (as shown by #BennyM) to OKObjectResult works.
Please note that the assertion would have succeeded had the controller method returned the ServiceResponse<Product> directly without modifying the return type on controller's method.
While this explains the behavior, I personally feel there is a better way to test controllers. This MSDN Article explains about integration testing. One can effectively unit test all the dimensions of the controllers - authentication, validation, (de)serialization, etc - by mocking the immediate dependencies of the respective controllers.
Since you are returning with an Ok call in your controller you can add a cast to the unit test.
var result = (await _productController.GetProduct(productId)).Result as OkObjectResult;
Assert.IsNotNull(result);
var returnedServiceResponse = result.Value as ServiceResponse<Product>;
Assert.That(returnedServiceResponse ?.Data?.Id, Is.EqualTo(productId));
Also you don't have to use Actionresult. You can also just return your service response
[HttpGet("{productId}")]
public async Task<ServiceResponse<Product>> GetProduct(int productId)
{
var result = await _productService.GetProductAsync(productId);
return result;
}
This will also make the test a bit easier as no need to use the OkObjectResult.
I have a class that gets knockout messages using the method below:
public IEnumerable<Message> GetKnockoutMessages(Data data)
{
if(data.Messages != null)
{
return data.Messages.Where(m => m.UnderWritingRuleId != null);
}
else
{
return new List<Message>();
}
}
So far, i have a unit test that only partially covers the unit test which is below.
[TestMethod]
public void TestGetKnockoutMessages()
{
KnockoutUtility knockUtility = new KnockoutUtility();
IEnumerable<Message> messages = knockUtility.GetKnockoutMessages(MockData.Object);
Assert.IsNotNull(messages);
}
This covers everything but return data.Messages.Where(m => m.UnderWritingRuleId != null);
I was wondering how I would unit test this method to get 100% coverage.
Any help would be appreciated. Thanks
This unit test doesn't cover everything because you are not verifying all cases. You have to test two logics:
if/else logic
Where(m => m.UnderWritingRuleId != null) logic
Based on this assumption you have to create cases which proof following:
If data.Messages are null then you have to verify that method returns new instance of List<Message>() because you specified in code like that.
Next case will be to verify that method returns empty IEnumerable<Message> if data.Messages are empty (which means this condition data.Messages.Count() == 0).
Next case will be to verify that method returns exact messages which satisfied your condition: Where(m => m.UnderWritingRuleId != null). In other words only messages which have populated UnderWritingRuleId.
To achieve what I mentioned you have to create Data objects which satisfied this cases. I don't know what is MockData.Object but I hope that you included you arrangement there.
Each case should be in separate test method like in following:
[TestMethod]
public void TestGetKnockoutMessages_Case1()
{
// Arrange
// Action
// Assert
}
[TestMethod]
public void TestGetKnockoutMessages_Case2()
{
// Arrange
// Action
// Assert
}
[TestMethod]
public void TestGetKnockoutMessages_Case3()
{
// Arrange
// Action
// Assert
}
Arrange is place where you configuring your mock-ups. Action is place where you executing method which you want to test and Assert is place where you doing assertion.
I have created a class which is as follows
public class Response<T> : IHttpActionResult where T : class
{
private readonly T _body;
....
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage msg = new HttpResponseMessage();
switch (_httpStatus)
{
case HttpResponseStatus.Ok:
{
msg = _request.CreateResponse(HttpStatusCode.OK, _body);
break;
}
case HttpResponseStatus.BadRequest:
{
msg = _request.CreateResponse(HttpStatusCode.BadRequest, _body);
break;
}
...
return Task.FromResult(msg);
}
}
This is my base class for returning IHTTPActionResult in my web api calls.
It works well for what i require it for.
I now have some unit tests setup using MSTest.
// Act
IHttpActionResult result = controller.Step1();
// Assert
Assert.IsNotNull(result, "Is null when it shouldnt be");
Assert.IsInstanceOfType(result, typeof(Response<BodyContent<LogingActivityResult>>), "Is not expected IsInstanceOfType");
Both of these Assertions work fine.
However i now want to actually get # the data held within my response and assert that they are ok i.e.: correct values, counts, etc but am having no luck with this.
I have tried ALL the various System.Web.Http.Results types such as
var contentResult = result as OkNegotiatedContentResult<Response<BodyContent<LogingActivityResult>>>;
or
var contentResult = result as FormattedContentResult<Response<BodyContent<LogingActivityResult>>>;
but both of these are null when i hover over contentResult variable.
Can anyone lead me in the right direction?
Thanks
I managed to solve this.. the property _body in my above class was set to PRIVATE, as a result I could not access this in my unit test class. Setting it to PUBLIC solved it and was able to access the data being returned in the response.
I have built unit testing for my service layer. I have not used Mock as I think that since you are adding/deleting/querying a database, why query a mock as the results could be different, but that isn't what I am asking.
Now I am using Moq to test my web api layer. I think that this is fine, as if all my tests pass on the service layer, it is fine to mock the services to test the web api.
I have managed to write a test for my GetAsync method and it works all fine, like so
Here is the controller:
public async Task<IHttpActionResult> GetAsync(long id)
{
Content content = await _service.GetAsync(id);
ContentModel model = Mapper.Map<ContentModel>(content);
return Ok(model);
}
Here is the test:
[TestMethod]
public void Content_GetAsync()
{
// arrange
var mockService = new Mock<IContentService>();
mockService.Setup(x => x.GetAsync(4))
.ReturnsAsync(new Content
{
Id = 4
});
// setup automapper
AutoMapperConfig.RegisterMappings();
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.GetAsync(4).Result;
var contentResult = actionResult as OkNegotiatedContentResult<ContentModel>;
// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual(4, contentResult.Content.Id);
}
I believe I wrote this correctly, and it seems to work. Now I would like to test my PostAsync method to add an item. The controller looks like this:
public async Task<IHttpActionResult> PostAsync(ContentModel model)
{
Content content = Mapper.Map<Content>(model);
await _service.AddAsync(content);
return Created<ContentModel>(Request.RequestUri, Mapper.Map<ContentModel>(content));
}
And here is the test:
[TestMethod]
public void Content_PostAsync()
{
var mockService = new Mock<IContentService>();
mockService.Setup(e => e.AddAsync(new Content()))
.ReturnsAsync(1);
// setup automapper
AutoMapperConfig.RegisterMappings();
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}).Result;
var contentResult = actionResult as CreatedAtRouteNegotiatedContentResult<ContentModel>;
// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual("New Heading", contentResult.Content.Heading);
}
Now when I run this, I get an error:
null reference exception. "Request" from the Request.RequestUri is null.
So I changed my controller and tests to this, to try and mock it.
Test code:
public Task<IHttpActionResult> PostAsync(ContentModel model)
{
return PostAsync(model, Request);
}
/// Unit testable version of above. Cannot be accessed by users
[NonAction]
public async Task<IHttpActionResult> PostAsync(ContentModel model, System.Net.Http.HttpRequestMessage request)
{
Content content = Mapper.Map<Content>(model);
await _service.AddAsync(content);
return Created<ContentModel>(request.RequestUri, Mapper.Map<ContentModel>(content));
}
Controller code:
[TestMethod]
public void Content_PostAsync()
{
// arrange
var mockRequest = new Mock<System.Net.Http.HttpRequestMessage>();
mockRequest.Setup(e => e.RequestUri)
.Returns(new Uri("http://localhost/"));
var mockService = new Mock<IContentService>();
mockService.Setup(e => e.AddAsync(new Content()))
.ReturnsAsync(1);
// setup automapper
AutoMapperConfig.RegisterMappings();
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}, mockRequest.Object).Result;
var contentResult = actionResult as CreatedAtRouteNegotiatedContentResult<ContentModel>;
// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual("New Heading", contentResult.Content.Heading);
}
Now I get an error saying:
Invalid setup on a non-virtual (overridable in VB) member: e => e.RequestUri
Can someone please, please help me with this. I am sure I am using Mock correctly in all tests, but unit testing is new to me, so maybe I am just not doing something right.
With Moq you can only mock virtual/absrtact members. The RequestUri is not a virtual member of HttpRequestMessage, hence the error message.
You should be able to just new a HttpRequestMessage directly without mocking it and pass that in.
var request = System.Net.Http.HttpRequestMessage>();
request.RequestUri = new Uri("http://localhost/");
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}, request).Result;
Ned's answer is correct. Moq is a constrained mocking library, meaning that it generates dynamic subclasses of the classes you mock at runtime. These subclasses cannot override methods if they are not declared virtual in the mocked class. You can find more information on constrained vs. unconstrained mocking libraries in the art of unit testing.
That's why people that use a mockist style of unit testing prefer to mock against interfaces instead of concrete classes, as the generated mock subclasses can easily override (or rather, implement) the methods on the interface.
Scenario: I am learning how to unit test. Currently am working on tests for an mvc action method with nUnit and FakeItEasy. I have a test to verify that the method throws an exception if passed an id that doesn't exist. The action method calls a repository wrapper method for .Single(), which will throw an exception if nothing is found. This is good.
In my test, I do the following:
Create fake IRepository using FakeItEasy
Create test data
Configure .Single() wrapper method to get data from my test data
Problem: I am having issues testing this. The problem is that when passed an invalid id, an exception is thrown right in the configuration code for the fake repository, instead of in the action method itself. The reason why is obvious. The configuration code is ran before the action method gets executed, and the configuration code calls .Single() on the test data... which (intentionally of course) does not contain the invalid id. So it throws an exception right then and there, and never even makes it to the action method. What I am not sure about, is how to get around this. The exception needs to be thrown inside the action method. I don't know how to configure the return value in a way that avoids this conundrum.
Code:
Controller Code
public ViewResult Details(int id)
{
var dbPart = _repository
.GetSingleRecord<Part>(x => x.PartID == id);
var viewmodel = new DetailsViewModel()
{
PartID = dbPart.PartID
};
return View(viewmodel);
}
Test Code
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
// Create a fake PartID that exists
partID_that_exists = 1;
// Create a fake PartID that doesn't exist
partID_that_doesnt_exist = -100;
}
[Test]
public void an_exception_is_thrown_if_the_part_doesnt_exist()
{
// Arrange
FakeRepository.FakePartID = partID_that_doesnt_exist;
_fakeRepository = FakeRepository.Create();
_controller = new PartController(_fakeRepository);
// Act & Assert
Assert.Throws<InvalidOperationException>(() =>
_controller.Details(partID_that_doesnt_exist));
}
Fake Repository Code
public class FakeRepository
{
public static int? FakePartID { get; set; }
public static IBasicRepository Create()
{
// Create fake repository
var fakeRepository = A.Fake<IBasicRepository>();
// Create fake test data
var fakeParts = new List<Part>()
{
new Part()
{
PartID = 1, PartDesc = "Fake Part 1"
},
new Part()
{
PartID = 2, PartDesc = "Fake Part 2"
}
};
// Configure fake repository to return fake data
A.CallTo(() => fakeRepository.GetAllRecords<Part>())
.Returns(fakeParts);
if (FakePartID.HasValue)
{
/* BELOW CODE IS THE PROBLEM */
A.CallTo(fakeRepository)
.Where(call => call.Method.Name == "GetSingleRecord")
.WithReturnType<Part>()
.Returns(fakeParts.Single(x => x.PartID == FakePartID));
}
// Return the newly created & configured fakeRepository
return fakeRepository;
}
}
I figured it out. I needed to use ReturnsLazily() instead of Returns().
ReturnsLazily delays setting the method's return values until the method is actually called, instead of setting them when the method's configuration code is executed.
New, Working Code:
A.CallTo(fakeRepository)
.Where(call => call.Method.Name == "GetSingleRecord")
.WithReturnType<Part>()
.ReturnsLazily(() => fakeParts
.Single(x => x.PartID == FakePartID));