ASP.NET MVC Outbound Url tests with NSubstitute

Posted by ben 2. June 2011 01:47

Today I needed to write some tests to verify my routing configuration in an ASP.NET MVC Application.

For my inbound route tests I used the MvcContrib TestHelper library. This has a handy extension method “ShouldMapTo” that checks a virtual path against your routing table:

	[Test]
	public void Projects_root() {
		"~/projects".ShouldMapTo<ProjectsController>(x => x.Index(1));
	}

The library also comes with a method for testing outbound Url generation. For example:

	OutBoundUrl.Of<ProjectController>(x => x.Index()).ShouldMapToUrl("/projects")

Under the covers this is mocking up a http context to generate the Url.

Since this helper method requires a strongly typed reference to the controller action it causes a problem when we have non nullable/optional parameters on our actions where the default values are specified within the route entry.

For example, take the following route entries:

	routes.MapRoute("projects/page/{page}", new { controller = "Projects", action = "Index" }, new { page = "\\d+" });
	routes.MapRoute("projects", new { controller = "Projects", action = "Index", page = "1" });

This route configuration allows me to use the Url /projects to go to page 1 of projects, or projects/page/2 to go to page 2.

Using the MvcContrib test helper I have to specify a value for page since it’s not a nullable parameter, rather than relying on the default values configured on the route entry.

So instead I decided to write my own outbound url test helper using NSubstitute. NSubstitute has become my first choice mocking/substitution framework as it’s just so damn easy to use.

The helper and some tests can be seen below (Note: I’m using NUnit):

	[SetUp]
	public void SetUp()
	{
		RouteTable.Routes.Clear();
		new RouteRegistry().RegisterRoutes(RouteTable.Routes);
	}

	private string GenerateUrl(object values)
	{
		var httpContext = Substitute.For<HttpContextBase>();
		var httpRequest = Substitute.For<HttpRequestBase>();
		var httpResponse = Substitute.For<HttpResponseBase>();
		httpResponse.ApplyAppPathModifier(Arg.Any<string>()).Returns(ctx => ctx.Arg<string>());
		httpContext.Request.Returns(httpRequest);
		httpContext.Response.Returns(httpResponse);           
		var requestContext = new RequestContext(httpContext, new RouteData());

		return UrlHelper.GenerateUrl(null, null, null,
			new RouteValueDictionary(values), RouteTable.Routes, requestContext, true);
	}

	[Test]
	public void Projects_root() {
		GenerateUrl(new { controller = "Projects", Action = "Index" })
			.ShouldEqual("/projects");
	}

	[Test]
	public void Projects_with_page() {
		GenerateUrl(new { controller = "Projects", Action = "Index", Page = 5 })
			.ShouldEqual("/projects/page/5");
	}

Tags: , ,
Categories C# | Development | ASP.NET | Testing

Executing future queries with NHibernate Linq

Posted by ben 10. May 2011 20:19

A while ago I posted about how we can make use of NHibernate future queries to perform efficient paging of data.

One problem with this approach is that we were using the Criteria API to define our queries, which prevented us from providing a “generic” paging method.

Since NH 3.0 we have been able to use the Futures feature on Linq queries. My original PagedList class used the Count() method to get the total number of records. Unfortunately, Count() executes immediately so we end up generating two queries.

Fortunately Diego has provided a handy extension method that supports future count queries:

    public static class NHibernateExtensions {
        public static IFutureValue<TResult> ToFutureValue<TSource, TResult>(
            this IQueryable<TSource> source,
            Expression<Func<IQueryable<TSource>, TResult>> selector)
            where TResult : struct {
 
            var provider = (NhQueryProvider)source.Provider;
            var method = ((MethodCallExpression)selector.Body).Method;
            var expression = Expression.Call(null, method, source.Expression);
            return (IFutureValue<TResult>)provider.ExecuteFuture(expression);
        }
    }

Since we need to apply ToFuture() after Skip() and Take() I had to change my PagedList class to support passing in a query that has already been paged and the total.

The updated class is as follows:

    public interface IPagedList<T> : IList<T> {
        int PageIndex { get; }
        int PageSize { get; }
        int TotalCount { get; }
        int TotalPages { get; }
        bool HasPreviousPage { get; }
        bool HasNextPage { get; }
    }

    public class PagedList<T> : List<T>, IPagedList<T> {
        
        public PagedList(IEnumerable<T> source, int pageIndex, int pageSize) :
            this(source.GetPage(pageIndex, pageSize), pageIndex, pageSize, x => x.Count()) {}
        
        public PagedList(IEnumerable<T> source, int pageIndex, int pageSize, Func<IEnumerable<T>, int> totalFunc) {
            var totalCount = totalFunc(source);
            this.TotalCount = totalCount;
            this.TotalPages = totalCount / pageSize;

            if (totalCount % pageSize > 0)
                TotalPages++;
            
            this.PageSize = pageSize;
            this.PageIndex = pageIndex;

            this.AddRange(source.ToList());
        }

        public int PageIndex { get; private set; }
        public int PageSize { get; private set; }
        public int TotalCount { get; private set; }
        public int TotalPages { get; private set; }

        public bool HasPreviousPage { get { return (PageIndex > 0); } }
        public bool HasNextPage { get { return (PageIndex + 1 < TotalPages); } }
    }

    public static class PageListExtensions {
        public static IEnumerable<T> GetPage<T>(this IEnumerable<T> source, int pageIndex, int pageSize) {
            return source.Skip(pageIndex * pageSize)
                .Take(pageSize);
        }
    }

I then created a single method that accepts any IQueryable<T> and returns a PagedList<T>:

	protected virtual IPagedList<T> FetchPaged(IQueryable<T> query, int pageIndex, int pageSize) {
		var futureCount = query.ToFutureValue(x => x.Count());
		return new PagedList<T>(query.Skip(pageIndex * pageSize).Take(pageSize).ToFuture(),
			pageIndex, pageSize, x => futureCount.Value);
	}

Finally a test to verify it works as expected:

	[Test]
	public void Can_get_paged_list_with_criteria() {
		for (var x = 0; x < 100; x++) {
			repository.Create(new TestEntity { Name = (x % 2 == 0) ? "Even" : "Odd"  });
		}

		var entities = repository.FetchPaged(t => t.Name == "Even", o => o.Asc(t => t.Name), 3, 10);
		entities.TotalCount.ShouldEqual(50);
		entities.TotalPages.ShouldEqual(5);
		entities.Count.ShouldEqual(10);
	}

Generates:

NHibernate: select cast(count(*) as INTEGER) as col_0_0_ from TestEntities testentity0_ where testentity0_.Name=@p0;
select testentity0_.Id as Id0_, testentity0_.Name as Name0_ from TestEntities testentity0_ where testentity0_.Name=@p1 order by testentity0_.Name asc limit 10 offset 30;
;@p0 = 'Even' [Type: String (0)], @p1 = 'Even' [Type: String (0)]

Tags:
Categories NHibernate | Development

Handling file uploads in complex view models

Posted by ben 20. April 2011 08:23

A while ago I posted how you can upload multiple files in ASP.NET MVC. You can see that post here.

Someone left a comment on that post asking how they might POST both data and files to a controller action (the example was to create a product and upload the product images).

Fortunately ASP.NET MVC makes this pretty trivial since the built in model binders can handle complex view models too.

So first let’s create our viewmodel. We will have properties for Name, Price and Images:

    public class ProductViewModel {
        public string Name { get; set; }
        public decimal Price { get; set; }
        public IEnumerable Images { get; set; }
    }

Then we create our controller GET and POST actions:

	public ActionResult NewProduct()
	{
		return View();
	}

	[HttpPost]
	public ActionResult NewProduct(ProductViewModel viewModel)
	{
		// save product 
		// var product = new Product { Name = viewModel.Name, Price = viewModel.Price });
		// repo.Save(product);

		// now do something with the files
		foreach (var file in viewModel.Images)
		{
			if (file.ContentLength > 0)
			{
				var fileName = Path.GetFileName(file.FileName);
				var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
				file.SaveAs(path);
			}
		}

		return RedirectToAction("Index");
	}

Our POST action has a parameter of type ProductViewModel. The modelbinder will handle wiring up the posted vales to our view model class.

Finally our view:

<form method="post" action="@Url.Action("NewProduct")" enctype="multipart/form-data">
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>ProductViewModel</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Price)
            @Html.ValidationMessageFor(model => model.Price)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Images)
        </div>
        <div class="editor-field">
            <input type="file" name="Images" />            
            <input type="file" name="Images" />
            <input type="file" name="Images" />
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
</form>

This time I haven’t bothered using the multiple file upload helper. Instead I’ve placed three file inputs all with the same name. Note that the name of the file inputs should correspond to the name of the IEnumerable<HttpPostedFileBase> property on your view model.

image

That’s really all there is to it.

Tags:
Categories ASP.NET | C# | Development