titaniumbunker.com

Evil geniuses and world domination are 2 of our goals... we also like Dr Who

Unit Testing your controllers

1 comment
Thomas thought that testing the controller would be a good thing.

Thomas thought that testing the controller would be a good thing.

I’m working on a home project at the moment – it’s an MVC/Entity Framework based project, and I have been stumped for the past 3 weeks on something – How can I test it?

So: For those that don’t know, Entity Framework is object-relational mapper that provides a simplified interface for developers to work with collections of objects close to the problem domain, rather than databases, keys and indexes.  There are apparently some purists who would suggest that writing tests for Entity Framework is out of scope for any project using it – and I would agree that writing tests for EntityFramework.dll would be pointless as it is not a system that you control.  I do believe that there is some scope for testing some of the use of Entity Framework within the scope of your application.  The relationship model for the entities that you define is still code, and therefore we should be looking to where possible test it.

Entity Framework ties strongly into the underlying database however.  Pointing Entity Framework against a list of in-memory objects doesn’t have the same effect as pointing EF to a database.  The tests and potentially the functionality that we define here would not necessarily perform exactly the same as the production system would.  For example – a child list is lazily loaded when entity Framework is pointed to a Database, but is eager loaded when pointing to an in-memory set.

So how can I test an MVC application that uses Entity Framework?

I read in interesting couple of articles here  : Three ways to test around entity framework and Entity Framework Testing with a Mocking Framework (EF6 onwards) and decided that I would write a proof of concept test for one of my working controllers using all three approaches :

  • InMemory DbSet
  • Moq
  • RimDev.Automation.Sql

 

The Product controller’s Create method, allows a user to post a form to the controller which contains information about a software product.  This software product can also include screenshots.  Screen shot uploading was implemented – after much wailing and gnashing of teeth – using DropZoneJS.

I heavily ‘borrowed’ from Khalid’s blog post using a fakeDBContext and managed to create a test that should add a product – and then I hit a snag – right where the code checks the Request.Files.Count attribute, to determine if a user has posted some screenshots.

public ActionResult Create([Bind(Include = "ID,Title,Description,Tagline,SourceCode,Lead,")] Product product)
        {
            if (ModelState.IsValid)
            {
                // add screenshots
                for (int i = 0; i < Request.Files.Count; i++)
                {
                    HttpPostedFileBase file = Request.Files[i];
                    if (file != null && file.ContentLength > 0)
                    {
                        file.InputStream.Position = 0;
                        Screenshot Screen = new Screenshot();
                        Screen.ProductID = product.ID;
                        Screen.Product = product;
                        using (var memoryStream = new MemoryStream())
                        {
                            file.InputStream.CopyTo(memoryStream);
                            Screen.ScreenShot1 = memoryStream.ToArray();
                        }
                        product.Screenshots.Add(Screen);
                    }
                }
                
                db.Products.Add(product);
                db.SaveChanges();

                if (Request.Files.Count == 0)
                {
                    return RedirectToAction("Index");
                }
                else
                {
                    var t = RedirectToAction("Index");
                    return Json(new { ErrorMessage = "", RedirectURL = Url.Action("Index",null, null, Request.Url.Scheme) });
                }
            }
            return View(product);
        }

 

NullReferenceException thrown when loading the request

NullReferenceException thrown when loading the request

So my controller had a dependancy on a request object – and that to me seemed somewhat reasonable.  I think it’s ok for a MVC controller that responds to a request to have some level of dependancy on that request.  Now I could check that the request object is not null – but the point is that there should be a request object – and in this instance a request of null is a valid error to throw.  It’s null in this instance because the test has created an instance of the controller, and there is no request behind it.

I didn’t want to modify the controller to check for this condition, as this would be changing production code- but not in a way that makes it more testable.

So a bit more research lead me to Scott Hanselman’s A Back To Basics Case Study: Implementing HTTP File Upload with ASP.NET MVC including Tests and Mocks.  In this article, Scott uses moq to create test mocks of the HttpRequestBase, the HttpContextBase, any posted files and attaches these mocked objects to the controller’s ControllerContext property.  Adding some of Scott’s code allowed the controller to execute

The code for this is considerable, and I feel that this will need to be cleaned up to make testing easier.  I think I’ll work at creating some form of object that can be easily mocked automatically based on a number of files that are to be uploaded – maybe I’ll even make a Nuget Package out of it.

 

So that’s problem solved right?

in the Entity Framework Testing with a Mocking Framework (EF6 onwards)  article,  the test checks that a blog can be added through the controller :

        [TestMethod] 
        public void CreateBlog_saves_a_blog_via_context() 
        { 
            var mockSet = new Mock<DbSet<Blog>>(); 
 
            var mockContext = new Mock<BloggingContext>(); 
            mockContext.Setup(m => m.Blogs).Returns(mockSet.Object); 
 
            var service = new BlogService(mockContext.Object); 
            service.AddBlog("ADO.NET Blog", "http://blogs.msdn.com/adonet"); 
 
            mockSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once()); 
            mockContext.Verify(m => m.SaveChanges(), Times.Once()); 
        } 

But the verification that this test is performed by checking that the Add method was called with any blog value once, and that the SaveChanges method was called once.  However reading through that, the controller code would pass the tests if the 2 lines were called in the wrong order – so this test potentially lets a malformed controller through the testing.  There was some work done on allowing moq to verify that actions were performed in sequence, which could potentially resolve this situation.  Some basic googling has revealed a project on GitHub – Moq-Sequences – that seems to offer the support for running methods in sequence that I was looking for.

 

 

One Response to “Unit Testing your controllers”

  1. […] a previous post I wrote about needing to fake up a HTTP Context for testing a controller and I hinted that perhaps […]



Categories

Archives

Tags