views:

98

answers:

1

I'm attempting to write a unit tests for an ASP.NET MVC 2 post action that takes a view model as its sole parameter. The view model is decorated with validation attributes such as [Required]. I'd like to test two scenarios. The first scenario is when a valid set of data is passed in (ie, all required properties have values) and a redirect to the list page is returned. The second scenario involves passing in invalid data (eg, when one or more of the Required properties are not set). In this case the same view is returned with error messages.

The action signature is as follows:

[HttpPost]
public virtual ActionResult Create(NewsViewModel model)

The NewsViewModel class is as follows:

public class NewsViewModel
{

    public Guid Id { get; set; }

    public DateTime? PublishStartDate { get; set; }

    public DateTime? PublishEndDate { get; set; }

    [Required]
    [StringLength(1000, 
        ErrorMessage = "Title must be less than 1000 characters.")]
    [HtmlProperties(Size = 100, MaxLength = 1000)]
    public string Title { get; set; }

    [Required]
    public string Content { get; set; }

    [DisplayName("Published")]
    [Required]
    public bool IsPublished { get; set; }

    [Required]
    public string Category { get; set; }

    public DateTime PublishedDateTime { get; set; }
}

My unit test for the first scenario is as follows:

[Test]
public void Create_Post()
{
    DateTime now = DateTime.Now;
    Guid id = Guid.NewGuid();

    // Act
    NewsViewModel viewModel = new NewsViewModel()
    {
        Id = id,
        Title = "Test News",
        Content = "this is the content",
        Category = "General",
        PublishedDateTime = now,
        PublishEndDate = now.Add(new TimeSpan(1, 0, 0)),
        PublishStartDate = now.Subtract(new TimeSpan(1, 0, 0))
    };

    ActionResult result = _controller.Create(viewModel);

    // Assert
    result.AssertActionRedirect();
}

The controller is created in the test setup using MvcContrib TestControllerBuilder:

private IWebDataService _webDataServiceFake;
private TestControllerBuilder _builder;
private PortalNewsController _controller;

[SetUp]
public void Setup()
{
    _webDataServiceFake = new WebDataService(new EntityDataServiceFake());
    _controller = new PortalNewsController(_webDataServiceFake);
    _builder = new TestControllerBuilder();
    _builder.InitializeController(_controller);
}

This test passes as expected, an action redirect back to the list page is returned. I'd like to create a clone of this whereby a number of the required properties are null or empty. In this case, the test should return return a view render action rather than a redirect. My attempt is as follows:

[Test]
public void Create_PostInvalid()
{
    DateTime now = DateTime.Now;
    Guid id = Guid.NewGuid();

    // Act
    NewsViewModel viewModel = new NewsViewModel()
    {
        Id = id,
        Title = null,
        PublishedDateTime = now,
        PublishEndDate = now.Add(new TimeSpan(1, 0, 0)),
        PublishStartDate = now.Subtract(new TimeSpan(1, 0, 0)),
        Content = null,
        Category = ""
    };

    ActionResult result = _controller.Create(viewModel);

    // Assert
    result.AssertViewRendered();
}

Even though I'm passing in a null for the Content property and an empty string for the Category property, the unit test is failing because the ViewState is indicating that the model is valid when I call the Create action.

I'm assuming that the validation code has not been called when I call the action, given that I'm calling it directly I'm not surprised. So how can I write a unit test that will perform all of the expected validation on the view model before I call the action method?

I'm using: ASP.NET MVC2, NUnit 2.5.7 and MvcContrib 2.0.96.0.

Thanks for any help. Glenn.

+1  A: 

I would recommend you reading Brad Wilson's excellent blog post on this topic. Basically it uses reflection to ensure that proper attributes are applied to your model and rely on the framework to do its job.

Darin Dimitrov