1

I have a form post to a procedure that I'd like to unit test with Moq. I have working code for unit testing Get WebApi methods which work great, but I'm not sure how to write up a post to a void method in an MVC controller.

I'd like to test the ModelState.IsValid and the exception returned if not valid.

CONTROLLER

public class HomeController : Controller
{
    private IEditDataRepository _editDataRepository;

    public HomeController()
    {
        //Default Constructor
    }

    //Dependency Injection using Unity.MVC5 NuGet Package
    public HomeController(IEditDataRepository editDataRepository)
    {
        _editDataRepository = editDataRepository;
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public void Edit([Bind(Include = "Field1,Field2")] FormViewModel model)
    {
        if (ModelState.IsValid)
        {
            _editDataRepository.Edit(model);
        }
        else
        {
            throw new HttpException(400, "ModelState Invalid");
        }
    }
}

REPOSITORY

public class EditDataRepository : IEditDataRepository, IDisposable
{
    private DBEntities db = new DBEntities();

    public void Edit(FormViewModel model)
    {            
        db.MyProcedure(model.Field1,model.Field2);
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                db.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

UNIT TESTS

I can do a little of the setup here, but I'm not sure after that. How can I check ModelState.IsValid and not valid (returns exception)?

[TestMethod]
public void TestSomething()
{
    //Arrange
    var mockRepository1 = new Mock<IEditDataRepository>();

    mockRepository1
       .Setup(x => x.Edit(It.IsAny<FormViewModel>()));
    HomeController controller = new HomeController(mockRepository1.Object);

    //Act
    controller.Edit(It.IsAny<FormViewModel>());

    //Assert

}
madvora
  • 1,717
  • 7
  • 34
  • 49
  • Just pass in an invalid `FormViewModel` to the Edit action. – Jamie Rees Apr 21 '16 at 15:59
  • Or set modelstate manually: http://stackoverflow.com/questions/3822873/how-to-mock-modelstate-isvalid-using-the-moq-framework – stephen.vakil Apr 21 '16 at 16:01
  • Curious, why is your action on the controller void? – Nkosi Apr 21 '16 at 16:23
  • Nkosi -not really sure. I guess I was just working off old code before I really knew what I was doing. What do you suggest this should be, ActionResult? I guess I did it because it's just running an update and I only needed to know if it failed. – madvora Apr 21 '16 at 17:10

2 Answers2

1
[TestMethod]
public void TestSomething()
{
    //Arrange
    var mockRepository1 = new Mock<IEditDataRepository>();

    mockRepository1
       .Setup(x => x.Edit(It.IsAny<FormViewModel>()));
    HomeController controller = new HomeController(mockRepository1.Object);
    controller.ModelState.AddModelError("error", "invalid model");

    //Act/Assert
    var ex = Assert.Throws<HttpException>(() => controller.Edit(It.IsAny<UTCFormViewModel>()));

    Assert.Equal(400, ex.ErrorCode);
}

Also, I'd suggest you don't implement the Dispose/Finalize pattern on your Repository. Unless it's really unmanaged resource which doesn't seem to be the case. This msdn link has more details.

Bruno Garcia
  • 6,029
  • 3
  • 25
  • 38
  • Great. Thanks for this. Is there a test for valid ModelState, or should I replace the void method with something else? – madvora Apr 21 '16 at 17:07
  • A test for valid model can be verified by inspecting your mock. It will be called case the model is valid – Bruno Garcia Apr 21 '16 at 17:19
0

I thought I'd share the route I went with this. I decided against returning void and decided to return an HttpStatusCode instead. I was already familiar with how to test that.

CONTROLLER

public class HomeController : Controller
{
    private IEditDataRepository _editDataRepository;

    //Dependency Injection using Unity.MVC5 NyGet Packages
    public HomeController(IEditDataRepository editDataRepository)
    {
        _editDataRepository = editDataRepository;
    }

    // GET: 
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "Field1,Field2")] FormViewModel model)
    {
        if (ModelState.IsValid)
        {
            _editDataRepository.Edit(model);
            return new HttpStatusCodeResult(HttpStatusCode.OK);
        }
        else
        {
            throw new HttpException(400, "ModelState Invalid");
        }
    }
}

REPOSITORY

public class EditDataRepository : IEditDataRepository, IDisposable
{
    private DBEntities db = new DBEntities();

    public void Edit(FormViewModel model)
    {            
        db.MyProcedure(model.Field1,model.Field2);
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                db.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

UNIT TESTS

[TestClass]
public class HomeControllerTests
{
    /// <summary>
    /// Tests the Edit method is run
    /// </summary>
    [TestMethod]
    public void Edit_Method_Is_Run()
    {
        //Arrange
        var mockRepository = new Mock<IEditDataRepository>();
        mockRepository
             .Setup(x => x.Edit(It.IsAny<FormViewModel>()));
        HomeController controller = new HomeController(mockRepository.Object);

        //Act
        controller.Edit(It.IsAny<FormViewModel>());

        //Assert
        mockRepository.VerifyAll();
    }

    [TestMethod]
    public void Edit_Returns_OK()
    {
        //Arrange
        var mockRepository = new Mock<IEditDataRepository>();
        mockRepository
             .Setup(x => x.Edit(It.IsAny<FormViewModel>()));
        HomeController controller = new HomeController(mockRepository.Object);

        //Act            
        var response = controller.Edit(It.IsAny<FormViewModel>());

        //Assert
        Assert.IsInstanceOfType(response, typeof(HttpStatusCodeResult));
        var httpResult = response as HttpStatusCodeResult;
        Assert.AreEqual(200, httpResult.StatusCode);
    }

    /// <summary>
    /// Tests the Edit method throws exception
    /// </summary>
    [TestMethod]
    [ExpectedException(typeof(HttpResponseException))]
    public void Edit_Returns_Exception()
    {
        var mockRepository = new Mock<IEditDataRepository>();
        mockRepository
            .Setup(x => x.Edit(It.IsAny<FormViewModel>()))
            .Throws(new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest)));
        HomeController controller = new HomeController(mockRepository.Object);

        //Act            
        var response = controller.Edit(It.IsAny<FormViewModel>());

        //Assert
        Assert.IsInstanceOfType(response, typeof(HttpResponseException));
    }
}
madvora
  • 1,717
  • 7
  • 34
  • 49