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
1234567891011121314151617181920 <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #2b91af">HttpContextBase</span> FakeAuthenticatedHttpContext(){<span style="color: #0000ff">var</span> context = <span style="color: #0000ff">new</span> <span style="color: #2b91af">Mock</span><<span style="color: #2b91af">HttpContextBase</span>>();<span style="color: #0000ff">var</span> request = <span style="color: #0000ff">new</span> <span style="color: #2b91af">Mock</span><<span style="color: #2b91af">HttpRequestBase</span>>();<span style="color: #0000ff">var</span> response = <span style="color: #0000ff">new</span> <span style="color: #2b91af">Mock</span><<span style="color: #2b91af">HttpResponseBase</span>>();<span style="color: #0000ff">var</span> session = <span style="color: #0000ff">new</span> <span style="color: #2b91af">Mock</span><<span style="color: #2b91af">HttpSessionStateBase</span>>();<span style="color: #0000ff">var</span> server = <span style="color: #0000ff">new</span> <span style="color: #2b91af">Mock</span><<span style="color: #2b91af">HttpServerUtilityBase</span>>();<span style="color: #0000ff">var</span> user = <span style="color: #0000ff">new</span> <span style="color: #2b91af">Mock</span><<span style="color: #2b91af">IPrincipal</span>>();<span style="color: #0000ff">var</span> identity = <span style="color: #0000ff">new</span> <span style="color: #2b91af">Mock</span><<span style="color: #2b91af">IIdentity</span>>();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(<span style="color: #0000ff">true</span>);identity.Expect(id => id.Name).Returns(<span style="color: #a31515">"test"</span>); <span style="color: #008000"></span> <span style="color: #0000ff">return</span> context.Object;}
12345678910 <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span>SetFakeAuthenticatedControllerContext(<span style="color: #0000ff">this</span> <span style="color: #2b91af">Controller</span> controller){<span style="color: #0000ff">var</span> httpContext = FakeAuthenticatedHttpContext();<span style="color: #2b91af">ControllerContext</span> context = <span style="color: #0000ff">new</span> <span style="color: #2b91af">ControllerContext</span>(<span style="color: #0000ff">new</span> <span style="color: #2b91af">RequestContext</span>(httpContext,<span style="color: #0000ff">new</span> <span style="color: #2b91af">RouteData</span>()), 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
123456 <span style="color: #0000ff">private</span> <span style="color: #2b91af">FakeViewEngine</span> _fakeViewEngine;<span style="color: #0000ff">public</span> <span style="color: #2b91af">FakeViewEngine</span> FakeViewEngine{<span style="color: #0000ff">get</span> {<span style="color: #0000ff">if</span> (_fakeViewEngine == <span style="color: #0000ff">null</span>)
12345678910111213141516171819 _fakeViewEngine = <span style="color: #0000ff">new</span> <span style="color: #2b91af">FakeViewEngine</span>();<span style="color: #0000ff">return</span> _fakeViewEngine;}}<span style="color: #0000ff">private</span> <span style="color: #2b91af">MessageController</span> authenticatedController;<span style="color: #0000ff">private</span> <span style="color: #2b91af">MessageController</span> AuthenticatedController{<span style="color: #0000ff">get</span> {<span style="color: #0000ff">if</span> (authenticatedController == <span style="color: #0000ff">null</span>){authenticatedController = <span style="color: #0000ff">new</span> <span style="color: #2b91af">MessageController</span>();authenticatedController.ViewEngine = FakeViewEngine;authenticatedController.SetFakeAuthenticatedControllerContext();}<span style="color: #0000ff">return</span> authenticatedController;}}
3. I created my test method which is going to call a Create method in my MessageController and pass it a string.
12345678 [<span style="color: #2b91af">TestMethod</span>]<span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> Create_Message_Test(){AuthenticatedController.Create(<span style="color: #a31515">"This is a test message"</span>);<span style="color: #008000">//verify</span> <span style="color: #2b91af">Assert</span>.AreEqual(<span style="color: #a31515">"Json"</span>, FakeViewEngine.ViewContext.ViewName);<span style="color: #2b91af">Assert</span>.IsInstanceOfType(FakeViewEngine.ViewContext.ViewData,
12 <span style="color: #0000ff">typeof</span>(MyViewData));<span style="color: #2b91af">Assert</span>.IsTrue(((MyViewData)FakeViewEngine.ViewContext
12 .ViewData).isSuccessful);<span style="color: #2b91af">Assert</span>.IsNotNull(((MyViewData)FakeViewEngine
12345678 .ViewContext.ViewData).Id);<span style="color: #0000ff">using</span> (MyDataContext dc = <span style="color: #0000ff">new</span> MyDataContext()){<span style="color: #0000ff">var</span> query =dc.Messages.Where(m => m.MessageId ==((MyViewData)FakeViewEngine
123456789101112 .ViewContext.ViewData).Id);<span style="color: #008000">//verify it was added to the database</span> <span style="color: #2b91af">Assert</span>.AreEqual(1, query.Count());<span style="color: #008000">//delete it</span> dc.Messages.DeleteOnSubmit(query.First());dc.SubmitChanges();<span style="color: #008000">//verify it was delete</span> <span style="color: #2b91af">Assert</span>.AreEqual(0, query.Count());}}
Note that in the verification block, I verify:
- The view being rendered
- The returned type of the ViewData
- 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
LINQ to SQL Resources
Testing and Mocking Resources
- Moq: Linq, Lambdas and Predicates applied to Mock Objects
- Moq
- Rhino Mocks
- TDD: Test-Driven Development with Visual Studio 2008 Unit Tests