Showing posts with label moq. Show all posts
Showing posts with label moq. Show all posts

Friday, April 15, 2011

ASP.NET MVC 3 Sample Project Launched




Okay, after popular demand of I think 0 people, I’ve published my old demo MVC 1 project as an MVC 3 project. If you haven’t read any of the posts on the last demo project, check out this post. This project completely separates UI & C# code, so you only have 2 projects (Core & UI…not counting the Unit Tests project).

Basically, all I did was create a new MVC 3 project using Razor with Microsoft’s default template and deleted everything except the following:

  • Views folder
  • Root Default.aspx
  • global.asax (I did delete the global.asax.cs)
  • both web.configs

I referenced my Core, setup my views that match my Core project, and inherited from my global.cs in the global.asax. That was it!

You can download it via zip here: http://code.google.com/p/derans/downloads/list (the file is called DemoPhotographySite_v3.zip)

I feel like this project will act as a great stepping stone to understanding the Code Camp server, which is much more complex.

The sample project was built with the following tools:

If you downloaded the old one, you’ll notice that I removed the following 3rd parties:

The primary reason I removed these three parties is because MVC 3 and Razor are good enough so you don’t need the 3rd party tools.

The best practices I mentioned above come straight from experience and the following people/resources:

You can see the exact same Core code in use at sweetandhappy.com. If you see any improvements that can be made or you’d just like to comment, please do so!

Also, please note that I basically am even re-posting the exact same blog post I did over a year ago with my original MVC demo project. I hope you don’t mind, but it’s late and I have to work tomorrow :)

Thanks for reading!

Shout it

kick it on DotNetKicks.com

Monday, May 17, 2010

Testing MVC 2.0 Controllers with Moq & NUnit




So I’ve been learning more and more about how to properly test my controllers in my MVC projects and I thought I’d share. I started using Moq recently and really like the syntax. It comes more natural to me over Rhino Mocks, which is what I was using before. I also started taking advantage of the MvcContrib test helpers, which are really nice! Of course I’m using NUnit, which seems to be what just about everyone uses. On a side note, testing is one of the major reasons to switch over to MVC from web forms. If you’re not using MVC now, I highly recommend switching over. You will not be disappointed.

Alright, well I’m going to be writing the tests for the contact form I blogged about back in March. Let’s get started.

First let’s test the Index() for the ContactController.

    [TestFixture]
public class ContactControllerTests
{
[
Test]
public void Index_Should_Return_Type_Of_ContactView()
{
//Arrange
var result = new ContactController();
//Act
result.Index();
//Assert
Assert.That(result.ViewData.Model.GetType(), Is.EqualTo(typeof(ContactView)));
}
}

The general rule of thumb for a unit test is Arrange, Act, & Assert. There generally should only be one assert per test, although I don’t think it’s set in stone. I’m sure some people disagree with that statement. Anyhow, this test is simple enough. Create a new instance of the controller, call the Index() action and assert that the returned model type is of ContactView. Done.

So I start on the next test and realize I’m going to need to use the SetUp attribute because I’m going to have some recurring items. Okay, so that looks like this:

        private ContactController _contactController;
[
SetUp]
public void Setup()
{
_contactController =
new ContactController();
}

So the second test looks like this:

       [Test]
public void Send_Should_Return_ContactView_If_ModelState_Has_Error()
{
_contactController.ModelState.AddModelError(
"testerror", "test error");
_contactController.Send(
new ContactView());

Assert.That(_contactController.ViewData.Model.GetType(), Is.EqualTo(typeof(ContactView)));
}

Basically, this adds an error and calls the Send() action and finally we assert that the ContactView is passed back. Here’s where we could add another Assert that checks the IsValid on the modelstate too, but I didn’t.

Next test looks like this:

        [Test]
public void Send_Should_Call_NotificationService_If_ModelState_IsValid()
{
var notificationService = new Mock<INotificationService>();
ObjectFactory.Inject(typeof(INotificationService), notificationService.Object);
_contactController.Send(
new ContactView());

notificationService.Verify(service => service.Notify());
}

So I use Mock<> to mock the notification service and then the StructureMap ObjectFactory to inject the mock in and then call the Send() action. Finally, I assert that Notify() was called on the mock.

Next I test to make sure the Success is passed in. Looks like this:

        [Test]
public void Send_Should_Call_NotificationService_If_ModelState_IsValid_And_Return_Success()
{
_contactController.Send(
new ContactView());

Assert.That(_contactController.ViewData["Success"], Is.True);
}

Then I test if the exception is thrown and it looks like this:

        [Test]
public void Send_Should_Call_NotificationService_And_Throw_A_NotificationException_If_Send_Fails()
{
_notificationService.Setup(service => service.Notify()).Throws(
new NotificationException());
_contactController.Send(
new ContactView());

Assert.That(_contactController.ModelState.IsValid, Is.False);
}

Notice I had to put the notification service in the SetUp too. Now my setup looks like this:

        private Mock<INotificationService> _notificationService;
private ContactController _contactController;
[
SetUp]
public void Setup()
{
_notificationService =
new Mock<INotificationService>();
ObjectFactory.Inject(typeof(INotificationService), _notificationService.Object);

_contactController =
new ContactController();
}

Finally I test to make sure everything comes back okay and the ContactView is passed back too. Looks like this:

        [Test]
public void Send_Should_Return_ContactView_If_No_Errors_Occur()
{
_contactController.Send(
new ContactView());

Assert.That(_contactController.ViewData.Model.GetType(), Is.EqualTo(typeof(ContactView)));
}

I also refactored the IsValid ModelState test to this:

        [Test]
public void Send_Should_Call_NotificationService_If_ModelState_IsValid()
{
_contactController.Send(
new ContactView());

_notificationService.Verify(service => service.Notify());
}

All our tests complete and green…

image

Thanks for reading!

Download the Source Here.


Shout it

kick it on DotNetKicks.com

Wednesday, April 28, 2010

Coding Dojo XP Part 2: TDD & BDD with Nunit, Moq, WatiN, SpecFlow, and MVC 2.0


This post is a continuation of a 3 part series about my first coding dojo experience and what we created. I’m going to assume you’ve read part 1 of this series. Otherwise, you might be lost.

With that said, let me make a correction here to part 1 :)

The feature scenario should’ve been this:

Scenario: Sign Guestbook
Given I am on the sign guestbook page
When I have filled out all required information
When I press sign
Then I should be redirected to the full list of guestbook entries and see a success message

and not this:

Scenario: Sign Guestbook
Given I have filled out all required information
When I press sign
Then I should be redirected to the full list of guestbook entries and see a success message

This means our SignGuestbookDefinitions changed to this:

    [Binding]
public class SignGuestBookDefinitions
{
[
Given(@"I am on the sign guestbook page")]
public void GivenIAmOnTheSignGuestbookPage()
{
var browser = new IE();
browser.GoTo(
"http://localhost:2345/guestbook/sign");
Assert.That(browser.Title, Is.EqualTo("Sign Guestbook"));
}

[
When(@"I have filled out all required information")]
public void GivenIHaveFilledOutAllRequiredInformation()
{
ScenarioContext.Current.Pending();
}

[
When(@"I press sign")]
public void WhenIPressSign()
{
ScenarioContext.Current.Pending();
}

[
Then(@"I should be redirected to the full list of guestbook entries and see a success message")]
public void ThenIShouldBeRedirectedToTheFullListOfGuestbookEntriesAndSeeASuccessMessage()
{
ScenarioContext.Current.Pending();
}
}

Basically, I missed an important step that makes more sense now. Sorry about that!

Let’s go ahead and dig in. So last time I said I’d pick up with the unit tests necessary to get the next step definition working in our SpecFlow feature. So that’s where we are…

First, add a new class library project called Dojo.UnitTests and add a reference to moq, nunit.framework, and the Dojo.UI.

Now we’ll create a new file called GuestbookControllerTests. Basically we’re going to test our controller because that’s where we’re going to throw our view model to our view.

Second, write our test. Here’s what our first controller test looks like:

[TestFixture]
public class GuestbookControllerTests
{
[
Test]
public void Sign_viewdata_model_should_return_SignGuestbookView()
{
var guestbookController = new GuestbookController();
var result = guestbookController.Sign();

Assert.That(result.ViewData.Model, Is.TypeOf(typeof(SignGuestbookView)));
}
}

Now, to make it pass. We need to create a new class called SignGuestbookView, which we’ll place in our Dojo.UI project under the Models folder. For the time being, it doesn’t have any properties. We want to do the bare minimum to get the test to pass.

Run the test and it will fail stating that the value was actually null. So, we have to pass in this new class to our view. To do that, we edit the GuestbookController in our Dojo.UI project. It should now look like this:

        public ViewResult Sign()
{
return View(new SignGuestbookView());
}

Okay, now we’re getting somewhere. We have all green now. However, we don’t really have anything yet. We’re trying to meet the “I have filled out all required information” step definition, so what is all required information? Well, in this case, it’s Name, Email, Comments and all are required. So…let’s keep going.

Let’s go ahead and add our properties to our SignGuestbookView so it’ll look like this:

    public class SignGuestbookView
{
public string Name { get; set; }
public string Email { get; set; }
public string Comments { get; set; }
}

I’m not going to write a test to verify that the get set works for each one. Okay, so let’s create the view. I’ll use the MVC 2.0 syntax instead of my usual fluentHTML. So, here it is:

<%=Html.ValidationSummary(false, "Important Message") %>
<%
using (Html.BeginForm())
{
%>
<fieldset>
<
legend>Sign Guestbook</legend>
<
p><%=Html.LabelFor(f=>f.Name) %><br /><%=Html.TextBoxFor(f=>f.Name) %></p>
<
p><%=Html.LabelFor(f=>f.Email) %><br /><%=Html.TextBoxFor(f => f.Email)%></p>
<
p><%=Html.LabelFor(f=>f.Comments) %><br /><%=Html.TextAreaFor(f=>f.Comments) %></p>
<
input type="submit" value="Sign" />
</
fieldset>
<%} %>

I don’t think this needs any explanation, but basically it uses the model, SignGuestbookView, passed in from the controller. The f in the lambda expression represents the model. I’ve also seen it model=>model.Name, etc, but I like the f because it’s short and to me represents field. When we hit play and browse to guestbook/sign now, we get this view:

image

So now we can go back to our feature step definitions (SignGuestbookDefinitions.cs in the Dojo.Features project) and play with some more WatiN.

So back in our SignGuestbookDefinitions class, in the GivenIHaveFilledOutAllRequiredInformation step, we’re going to fill in our textboxes. It’s pretty easy, here it is:

[When(@"I have filled out all required information")]
public void GivenIHaveFilledOutAllRequiredInformation()
{
_browser.TextField(
Find.ByName("Name")).TypeText("John Doe");
_browser.TextField(
Find.ByName("Email")).TypeText("john@email.com");
_browser.TextField(
Find.ByName("Comments")).TypeText("John wuz here.");
}

That’s it! I love WatiN, it’s really cool for browser automation. OH! I should mention, I made the browser from the first step a private field. You’ll also notice that you don’t have to GoTo a URL again because the step definitions cascade down as I mentioned in the first post.

Okay, we have our next step complete. Now let’s implement the WhenIPressSign.

        [When(@"I press sign")]
public void WhenIPressSign()
{
_browser.Button(
Find.ByValue("Sign")).Click();
}

Done! Next please! Okay, now we’re going to get into some fun stuff. So we need to redirect to a full list of guestbook entries and show a success message. So let’s handle the redirect first. Let’s write the test. I’m going to be using the MvcContrib.TestHelper for these tests. Here it is:

        [Test]
public void Sign_should_redirect_when_no_model_errors_present()
{
var result = _controller.Sign(new SignGuestbookView());

result.AssertActionRedirect().ToAction(
"Index");
}

The AssertActionRedirect is part of the MvcContrib.TestHelper…VERY HANDY! Make sure to download the new version that is compatiable with MVC 2.0. So when we run this, it fails because we’re not redirecting currently, so let’s fix that problem.

        [HttpPost]
public RedirectToRouteResult Sign(SignGuestbookView model)
{
return RedirectToAction("Index");
}

Okay, run the test again and we get green! Now what? Well, let’s go check our step and see what we need to do next. We should show a success message and a full list of entries. Let’s take care of the success message now. Here’s the new test:

[Test]
public void Sign_should_redirect_when_no_model_errors_present_and_pass_a_success_flag()
{
var result = _controller.Sign(new SignGuestbookView());

result.AssertActionRedirect()
.WithParameter(
"showSuccess", true)
.ToAction(
"Index");
}

So, let’s implement this to work in the Sign(SignGuestbookView model) action:

        [HttpPost]
public RedirectToRouteResult Sign(SignGuestbookView model)
{
return RedirectToAction("Index", new {showSuccess = true});
}

Now we need to make a change to our Index action like this:

        public ActionResult Index(bool showSuccess)
{
if (showSuccess)
ViewData[
"Success"] = "true";

return View();
}

Okay, let’s setup the display message in the Index view. This view just has this:

<html xmlns="http://www.w3.org/1999/xhtml" >
<
head runat="server">
<
title>View Guestbook</title>
</
head>
<
body>
<
div>
<%=Html.DivSuccessMessage("Guestbook entry successfully added.") %>
</div>
</
body>
</
html>

The DivSuccessMessage is a helper I wrote to check the ViewData[“Success”]. You can read more about my favorite extensions in an old post. Okay, I think we’re done with the success message. So, now we need to show a full list of guestbook entries. Well, I’m not sure how to test this one without assuming we have guestbook entries, so I’ll handle that with unit tests and in our step definition, we’re just going to make sure we’re on the right page. So, let’s go ahead and setup our final step definition and run those tests. They should all pass now. Here’s the final implementation:

[Then(@"I should be redirected to the full list of guestbook entries and see a success message")]
public void ThenIShouldBeRedirectedToTheFullListOfGuestbookEntriesAndSeeASuccessMessage()
{
Assert.That(_browser.Title, Is.EqualTo("View Guestbook"));
Assert.That(_browser.Div(Find.ByClass("success-message")).Text, Is.EqualTo("Guestbook entry successfully added."));
}

So this verifies that we’re on the right page and that our success message is displayed. You should see this:

image

I love seeing green! So, obviously we’re not done with everything required for our feature, but we’re close and have the basics all setup and configured. What’s nice is that we have built and tested the functionality and the application is ignorant of our actual implementation…because right now we don’t have any implementation. That’s a good thing. We should be able to build a whole guestbook app without ever actually knowing where we’re going to store the data. I think this is probably the safest way to develop because you know you’re not tying your application to a particular implmentation. Anyhow…let’s keep going.

First things first, we need to save our SignGuestbookView in the Sign action. Here’s our first test:

[Test]
public void Sign_should_save_and_redirect_when_no_model_errors_present()
{
var result = _controller.Sign(new SignGuestbookView());
_repository.Verify(rep=>rep.Save(
new GuestbookEntry()));

result.AssertActionRedirect().ToAction(
"Index");
}

I’m using moq to verify that a save method was called on our repository. Let’s make this pass. First we need a repository…should look like this:

    public interface IGuestbookRepository
{
void Save(GuestbookEntry model);
}

Now we need the GuestbookEntry, which is our domain model representation of a guestbook entry.

    public class GuestbookEntry
{
public string Name { get; set; }
public string Email { get; set; }
public string Comments { get; set; }
}

Now we need to define the repository in our test class like this:

private readonly Mock<IGuestbookRepository> _repository = new Mock<IGuestbookRepository>();

Okay, now we need to implement our controller action like so:

        public ActionResult Sign(SignGuestbookView model)
{
if (ViewData.ModelState.IsValid)
{
_repository.Save(model.CreateGuestbookEntry());
return RedirectToAction("Index", new {showSuccess = true});
}

return View(model);
}

Now we have to pass in our _repository in the controller’s constructor like this:

        private readonly IGuestbookRepository _repository;
public GuestbookController(IGuestbookRepository repository)
{
_repository = repository;
}

In order to finally get it to pass, I had to override the equals on the GuestbookEntry so moq could pick up the Save actually being called. So here are the tests for that implementation:

[TestFixture]
public class GuestBookEntryTests
{
[
Test]
public void GuestbookEntry_should_equal_another_GuestbookEntry_if_email_addresses_match()
{
var entry = new GuestbookEntry();

Assert.That(entry, Is.EqualTo(new GuestbookEntry()));
}

[
Test]
public void GuestbookEntry_should_not_equal_another_GuestbookEntry_if_email_addresses_do_not_match()
{
var entry = new GuestbookEntry();

Assert.That(entry, Is.Not.EqualTo(new GuestbookEntry{Email = "donotmatch@email.com"}));
}
}

Here’s the implementation:

        public override bool Equals(object obj)
{
var other = obj as GuestbookEntry;
return other != null && other.Email == Email;
}

Okay, so now all we need to do is display the guestbook entries. Well, here’s the test:

[Test]
public void Index_should_return_all_entries()
{
_repository.Setup(rep => rep.GetAll()).Returns(
new List<GuestbookEntry> {new GuestbookEntry()});
var result = _controller.Index(false);

Assert.That(result.AssertViewRendered().ViewData.Model, Is.TypeOf(typeof (List<GuestbookEntry>)));
Assert.That(((IList<GuestbookEntry>)result.AssertViewRendered().ViewData.Model).Count, Is.EqualTo(1));
}

I’m using moq again here to GetAll from my repository and I return a list with just one GuestbookEntry in it. I call the Index action and pass in false because I don’t care about showing the success message. Then I assert that the model is of type list<guestbookentry> then I assert that there is one record in the list.

So, let’s get this test to pass. We need to add a new method to the repository called GetAll like this:

    public interface IGuestbookRepository
{
void Save(GuestbookEntry model);
IList<GuestbookEntry> GetAll();
}

Also, just FYI, I added a new project called Dojo.Core and added a domain folder & interfaces folder and put the GuestbookEntry and IGuestbookRepository in that project. Anyhow, let’s keep going…so now all we need to do is implement the index action like this:

       public ActionResult Index(bool showSuccess)
{
if (showSuccess)
ViewData[
"Success"] = "true";

return View(_repository.GetAll());
}

Now we have all green. Alright, that’s it for this one. Feels good to see this:

image

Next post will have some of the implementation items in it. I probably won’t post every little detail like I have in this one and part 1, but I will provide the downloadable source. I did not get to the EmitMapper because I ended up just creating the CreateGuestbookEntry() on the view. I actually think I may have left that out of this post, but it is in the downloadable project.

Please note that when downloading the projects, you need to have SpecFlow installed, VS 2008 SP1 & MVC 2.0. Also, I’m going to add in a dummy repository and an empty constructor to the GuestbookController so the project will run without errors when you download it. I’ll tie in StructureMap in the next post.

I hope you enjoyed the post. Thanks for reading!

Download the Part 2 Solution

Shout it

kick it on DotNetKicks.com

Related Posts Plugin for WordPress, Blogger...