Mathias Brandewinder on .NET, F#, VSTO and Excel development, and quantitative analysis / machine learning.
by Mathias 26. December 2010 11:38

A little while ago, I looked into dynamically sorting a list, based on a criterion supplied by the user. There was one limit on the solution I presented, however: only one criterion could be applied at a time. What if we wanted to sort by multiple criteria?

For instance, using the same example as in the previous post, if we had a list of Products with a Name, a Supplier and a Price, how would we go about sorting by Supplier and then by Price? Or by Name and then Supplier? And how could we make that flexible, so that we can sort by an arbitrary number of criteria. This would be particularly useful if we had in mind the development of a user interface where users could select how they want to see a list of items displayed on screen.

Besides OrderBy, LINQ provides the ThenBy extension method. ThenBy is applied to an IOrderedEnumerable – a collection that has already been ordered – and sorts its, maintaining the existing order. For instance, in the following example, products are first sorted by Supplier, and for each Supplier, by Price:

private static void Main(string[] args)
{
   var apple = new Product() { Supplier = "Joe's Fruits", Name = "Apple", Price = 1.5 };
   var apricot = new Product() { Supplier = "Jack & Co", Name = "Apricot", Price = 2.5 };
   var banana = new Product() { Supplier = "Joe's Fruits", Name = "Banana", Price = 1.2 };
   var peach = new Product() { Supplier = "Jack & Co", Name = "Peach", Price = 1.5 };
   var pear = new Product() { Supplier = "Joe's Fruits", Name = "Pear", Price = 2 };

   var originalFruits = new List<Product>() { apple, apricot, banana, peach, pear }; 

   Func<Product, IComparable> sortByPrice = (product) => product.Price;
   Func<Product, IComparable> sortByName = (product) => product.Name;
   Func<Product, IComparable> sortBySupplier = (product) => product.Supplier;

   var sortedFruits = originalFruits
      .OrderBy(sortBySupplier)
      .ThenBy(sortByPrice);

   foreach (var fruit in sortedFruits)
   {
      Console.WriteLine(string.Format("{0} from {1} has a price of {2}.",
         fruit.Name,
         fruit.Supplier,
         fruit.Price));
   }

   Console.ReadLine();
}

More...

by Mathias 12. December 2010 17:00

I recently began playing with ASP.NET MVC (never too late), and so far I really enjoy it. One aspect I really appreciate is its testability – I can write fairly straightforward unit tests to verify that the application behaves as I believe it should, as well as make sure I understand what is going on.

One point which got me stumped was how to test for authorization. A controller, or some of its methods, can be decorated with the attribute [Authorize], restricting users who can access the method by role or name. In the default ASP.NET MVC 2 template, when a user isn’t authorized to a specific area, he gets re-directed to the LogOn method on the AccountController.

So far so good. However, I ran into unexpected issues when I attempted to unit test that. Suppose there are two roles in our web application, Chicken and Pigs, and that we have a Controller that leads to a pigs-only area of the web site:

[Authorize(Roles = "Pig")]
public class PigsOnlyController : Controller
{
   public ActionResult Index()
   {
      return View("Index");
   }
}

My first thought was to mock the ControllerContext and do something along these lines:

[Test]
public void OnlyPigsShouldAccessIndex()
{
   var context = new Mock<ControllerContext>();
   var userName = "PIG";
   context.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(userName);
   context.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
   context.Setup(p => p.HttpContext.User.IsInRole("Pig")).Returns(true);

   var controller = new PigsOnlyController();
   controller.ControllerContext = context.Object;

   // check what controller.Index() returns
}

However, while the web application itself behaved properly (Users in the Pig role get to the Index page, whereas Chicken get redirected to the Logon page), the test wasn’t doing what I expected: both Pigs and Chicken were happily reaching the Pigs-Only area.

As is often the case, I found out why on StackOverflow; the reason is that the re-direction is not the responsibility of the Controller. If the controller is properly decorated, the Index method won’t even be invoked, and where the call gets redirected to is handled in a different part.

So how do you unit test this behavior? In this case, we trust the framework to handle the redirection, so the only functionality we need to ascertain is that the Controller has the proper attribute.

Instead of validating the redirection, we can verify that the Controller class has an Authorize attribute, with the proper roles specified:

[Test]
public void ControllerShouldAuthorizePigsButNotChicken()
{
   var information = typeof(PigsOnlyController);
   var attributes = information.GetCustomAttributes(typeof(AuthorizeAttribute), false);
   Assert.AreEqual(1, attributes.Length);

   var authorization = attributes[0] as AuthorizeAttribute;
   var authorizedRoles = authorization.Roles;

   var roles = authorizedRoles.Split(',');
   Assert.IsTrue(roles.Contains("Pig"));
   Assert.IsFalse(roles.Contains("Chicken"));
}

I am not totally happy with the hard-coded strings “Pig” and “Chicken” in the test, but I don’t see a way around it; maybe it’s a sign that this test is more of an integration test than a unit test? If you know of a better way to test for that aspect, I am all ears!

Comments

Comment RSS