Unit Test Linq to Sql in ASP.Net MVC with Moq

I have just spent the entire day playing with Moq to unit test an asp.net mvc application I am working with. All I wanted to do is test a “create” method that simply adds a record to the database. So here it goes.

1. I created a Mock Http context to be used by my controller. I modified the Moq version of the MvcMockHelpers class from Scott Hanselman and added two more methods to mock an authenticated user

public static HttpContextBase FakeAuthenticatedHttpContext()
{
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
var user = new Mock<IPrincipal>();
var identity = new Mock<IIdentity>();

context.Expect(ctx => ctx.Request).Returns(request.Object);
context.Expect(ctx => ctx.Response).Returns(response.Object);
context.Expect(ctx => ctx.Session).Returns(session.Object);
context.Expect(ctx => ctx.Server).Returns(server.Object);
context.Expect(ctx => ctx.User).Returns(user.Object);
user.Expect(ctx => ctx.Identity).Returns(identity.Object);
identity.Expect(id => id.IsAuthenticated).Returns(true);
identity.Expect(id => id.Name).Returns("test"); 
    return context.Object;
}
public static void
SetFakeAuthenticatedControllerContext(this Controller controller)
{
var httpContext = FakeAuthenticatedHttpContext();
ControllerContext context =                  
new ControllerContext(
new RequestContext(httpContext,
new RouteData()), controller);
controller.ControllerContext = context;
}

Note that the identity.Name returns “test” which is the username of an existing user in the database. If I don’t do that then the MembershipProvider.GetUser method will fail.

2. I added a couple of properties to my test class (MessageControllerTest) to make it easy for me to access the controller and view engine in all the test methods

private FakeViewEngine _fakeViewEngine;
public FakeViewEngine FakeViewEngine
{
get
    {
if (_fakeViewEngine == null)

              _fakeViewEngine = new FakeViewEngine();
return _fakeViewEngine;
}
}

private MessageController authenticatedController;
private MessageController AuthenticatedController
{
get
    {
if (authenticatedController == null)
{
authenticatedController = new MessageController();
authenticatedController.ViewEngine = FakeViewEngine;
authenticatedController.SetFakeAuthenticatedControllerContext();
}
return authenticatedController;
}
}

3. I created my test method which is going to call a Create method in my MessageController and pass it a string.

[TestMethod]
public void Create_Message_Test()
{
AuthenticatedController.Create("This is a test message");

//verify
    Assert.AreEqual("Json", FakeViewEngine.ViewContext.ViewName);
Assert.IsInstanceOfType(FakeViewEngine.ViewContext.ViewData,

                             typeof(MyViewData));
Assert.IsTrue(((MyViewData)FakeViewEngine.ViewContext

                            .ViewData).isSuccessful);
Assert.IsNotNull(((MyViewData)FakeViewEngine

                            .ViewContext.ViewData).Id);

using (MyDataContext dc = new MyDataContext())
{
var query =
dc.Messages.Where(
m => m.MessageId ==
((MyViewData)FakeViewEngine

                       .ViewContext.ViewData).Id);
//verify it was added to the database
        Assert.AreEqual(1, query.Count());

//delete it
        dc.Messages.DeleteOnSubmit(query.First());
dc.SubmitChanges();

//verify it was delete
        Assert.AreEqual(0, query.Count());
}
}

Note that in the verification block, I verify:

  1. The view being rendered
  2. The returned type of the ViewData
  3. Properties on the ViewData

Then I clean up the created message by deleting it from the database.

Important:

You must add your connection strings and membership definition in an app.config file in your test project. If you don’t then the default consrtuctor of your DataContext will fail to run because it looks for the connection string in the config file.

I am still wrapping my head around the concept of mocking, so any tips or advice will be appreciated.

Here are some good resources that I found during my struggle to get this to work.

ASP.NET MVC Resources

  1. ASP.NET MVC Framework – Part 2: Testing
  2. ASP.NET MVC Session at Mix08, TDD and MvcMockHelpers

LINQ to SQL Resources

  1. Being Ignorant with LINQ to SQL

Testing and Mocking Resources

  1. Moq: Linq, Lambdas and Predicates applied to Mock Objects
  2. Moq
  3. Rhino Mocks
  4. TDD: Test-Driven Development with Visual Studio 2008 Unit Tests
  • Pingback: Wöchentliche Rundablage: ASP.NET MVC, Silverlight 2, .NET, RegEx, .NET, Icons, CSS, UI | Code-Inside Blog

  • CannonFodder

    I think there’s an error in the AuthenticatedController property. It seems to reference controller which does not appear to be declared. Did you mean to use authenticatedController?

  • CannonFodder

    I think there’s an error in the AuthenticatedController property. It seems to reference controller which does not appear to be declared. Did you mean to use authenticatedController?

  • http://www.emadibrahim.com Emad Ibrahim

    @cannonfodder good catch, I meant to reference the private field authenticatedController. I fixed the code.

  • http://www.emadibrahim.com Emad Ibrahim

    @cannonfodder good catch, I meant to reference the private field authenticatedController. I fixed the code.

  • http://blogger.forgottenskies.com Steve

    Thank you – very helpful!

  • http://www.anydbtest.com dbunittest

    Hi,
    In my opinion, DB Unit Testing is DB developer's business, not Application developer's. At least it is true in my organization, we believe the common developers are not good at database design, especially in the modern OO era.

    We employed a powerful DB unit testing tool, it named as AnyDbTest. (http://www.anydbtest.com). By using this tool, we need NOT write Java/.Net DB unit testing code any more. We can configure one xml file to tell what we want to test and what is our assertion. Then AnyDbTest can do the rest things.

    Additionally, we can versioned control the xml-style test case just like the code files. It is very handy.

  • hatem

    hello
    ezyak ya emad
    ana delwa2ty 3andy application 3amlo asp.net mvc + subsonic to create DAL
    ana 3wz a3ml unit test 3la l controllers w ana ma3rafsh 2y 7aga fl testing wla 3omry 3amlto abl kda
    fa yaret te2oly a3mlha ezy bebasata l2n ana lsa babtedy fa bera7a 3la a5ok:D
    thnx in advance
    salam

  • hatem

    hello
    ezyak ya emad
    ana delwa2ty 3andy application 3amlo asp.net mvc + subsonic to create DAL
    ana 3wz a3ml unit test 3la l controllers w ana ma3rafsh 2y 7aga fl testing wla 3omry 3amlto abl kda
    fa yaret te2oly a3mlha ezy bebasata l2n ana lsa babtedy fa bera7a 3la a5ok:D
    thnx in advance
    salam