Reputation: 2218
How to do unit test for try-catch block? I am working on ASP.NET MVC 4.0 with Moq as unit test object. And I want to figure out how to cover the try-catch block.
Below is the screen shot for my controller's action method including try-catch block:
public ActionResult ProductDelete(int id)
{
try
{
ViewBag.Title = "Delete Product";
ProductDetailDto dto = ProductService.GetProductDetail(id);
return View("ProductDelete", BuildProductDetailModel(dto));
}
catch (Exception)
{
throw; // cannot test this line
}
}
You can see that when I enable the code coverage coloring option from test explorer then it's showing the catch block uncovered.
Below is the screen shot for my unit test method:
[TestMethod]
public void ProductDeleteTest()
{
ProductController controller = new ProductController();
var existingProductDetail = _ProductService.GetAllProductsDetails().ToList().FirstOrDefault();
if (existingProductDetail != null)
{
TestControllerBuilder builder = new TestControllerBuilder();
builder.InitializeController(controller);
// Act
var actual = controller.ProductDelete(existingProductDetail.ProductDetailId) as ViewResult;
var viewModel = (ProductDetailModel)actual.Model;
// Assert
Assert.IsNotNull(actual);
Assert.IsInstanceOfType(viewModel, typeof(ProductDetailModel));
Assert.AreEqual(actual.ViewName, "ProductDelete");
}
}
And I want to figure out how I can cover the try-catch block in unit testing method.
Upvotes: 2
Views: 8947
Reputation: 236268
Your current test has several problems
null
then you will have NullReferenceException
during accessing Model
property. And if model is not of ProductDetailModel
type, you will have cast exception.So, you should mock your dependencies, setup communication between your controller and mocks, and then act. Your test can look like (Moq library is used here):
private ProductController controller;
private Mock<IProductService> productServiceMock;
private Random random = new Random();
[TestInitialize]
public void Init()
{
productServiceMock = new Mock<IProductService>();
controller = new ProductController(productServiceMock.Object);
}
[TestMethod]
public void ShouldReturnDetailsOfDeletedProduct()
{
int id = random.Next();
var dto = new ProductDetailDto { ProductDetailId = id };
productServiceMock.Setup(s => s.GetProductDetail(id)).Returns(dto);
// Act
var actual = controller.ProductDelete(id) as ViewResult;
var viewModel = (ProductDetailModel)actual.Model;
// Assert
Assert.AreEqual("ProductDelete", actual.ViewName);
Assert.AreEqual(id, viewModel.Id);
productServiceMock.VerifyAll();
}
As you can see, mocked service is used here. You setup which dto to return, and you verify if service was called. Also keep in mind, that in assertion you put expected value first, and then goes actual value.
So, here is how you can setup exception throw with mocks:
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldNotReturnResultWhenExceptionOccurs()
{
int id = random.Next();
var dto = new ProductDetailDto { ProductDetailId = id };
productServiceMock.Setup(s => s.GetProductDetail(id))
.Throws(new ArgumentException());
// Act
controller.ProductDelete(id);
}
Setup service mock to throw some exception, and then use ExpectedException
attribute to verify your controller re-thrown exception from service.
Upvotes: 4
Reputation: 6992
You want to be explicit on which line throw the exception in the try block. Then use the below approach
Assert exception from NUnit to MS TEST
UPDATE: Sorry was in the train so I was hurry to write it down. Here is complete answer
Your test should be simple as this
[TestMethod]
public void ProductDeleteAction_GetProductDetail_ThrowsException()
{
// Arrange
var productServiceMock = new Mock<IProductService>();
productServiceMock.Setup(x => x.GetProductDetail(It.IsAny<int>())).Throws(new Exception("some"));
var sut = new ProductController(productServiceMock.Object);
// Act
AssertException.Throws<Exception>(() => sut.ProductDelete(It.IsAny<int>()));
}
I would also create a simple helper method as I pointed out before
public static class AssertException
{
public static T Throws<T>(Action action) where T : Exception
{
try
{
action();
}
catch (T ex)
{
return ex;
}
Assert.Fail("Expected exception of type {0}.", typeof(T));
return null;
}
}
Your Controller):
public ProductController(IProductService productService) {
_productService = productService;
}
public ActionResult ProductDelete(int id)
{
try
{
ViewBag.Title = "Delete Product";
ProductDetailDto dto = _productService.GetProductDetail(id);
return View("ProductDelete", BuildProductDetailModel(dto));
}
catch (Exception)
{
throw;
}
}
There are few things to note:
Your test method name is poorly written. Avoid including "Test" method name as you know it is a Unit Test. Always use a very readable test method name. You have an "if" statement in your Unit Test. Avoid any logic in your test, this can cause bugs in your test. Your tests should be 'very' simple. Since you are want to test the exception, your asserts are irrelevant. They can be in a separate Unit test.
Upvotes: 0