Uncategorized

2015, this blog in review

The WordPress.com stats helper monkeys prepared a 2015 annual report for this blog.

Here’s an excerpt:

The concert hall at the Sydney Opera House holds 2,700 people. This blog was viewed about 24,000 times in 2015. If it were a concert at Sydney Opera House, it would take about 9 sold-out performances for that many people to see it.

Click here to see the complete report.

Advertisement

Write Less, Test More (part 2)

In my previous post I blogged about Xunit Theories, which provided a neat way to pass multiple test scenarios into one test method. Why I’m liking Xunit these days is due to its extensibility. In particular Adam Ralph and friends have extended it and come up with Xbehave.Net.

A popular pattern when testing is Given (my input), When (I test this), Then (expect this outcome). See Cucumber scenarios for more info. One of the problems with my last post is that the [Fact] didn’t use this language. The test output didn’t use this language. Wouldn’t it be nice if it did?

If we wrote that test with xBehave it would. This post revisits the problem from the first post and shows how we could test it using xBehave.Net.

Problem Recap

I want a class that, when given a sentence, will return a collection of strings containing all words in lowercase. This collection should only have alpha characters in it – i.e. spaces and punctuation are to be ignored.

To write our test for the zero case, using xBehave it could look like:

[Scenario]
public void FindWordsEmptyStringScenario(string input, IEnumerable output)
{
"Given an Empty String"
   .Given(() => input = string.Empty);

"When I find words in this string"
   .When(() => output = _wordFinder.FindAllWordsInLowerCase(input));

"Then I expect the answer to be an empty list"
   .Then(() => Assert.Empty(output));
}

Cucumber uses scenarios, so does our test. The test method, takes as its inputs an input and output variable. The method cleanly has three calls, each coming off a string describing the action:
Spelling it out, the Given() sets up our input variable.
When(), does the business of calling our method under test, returning the output, into our variable, called output.
Then(), asserts if the output is what we expected.

Talk about readable!

It gets better – run this through reSharper and our output looks like:

zero_xunit

Again very readable I think.

As I’m sure you guessed, this framework supports something very similar to Xunit’s [InlineData]. It has examples.

Our test scenario, with all example inputs and outputs, based on first blog, looks like

[Scenario]
[Example("", new string[0])]
[Example("One", new [] { "one" })]
[Example("One two", new[] { "one", "two" })]
[Example("One...:; - ,two", new[] { "one", "two" })]
public void FindAllWordsInLowerCaseScenario(string input, string[] expectedOutput, IEnumerable output)
{
    "Given the sentence '{0}'"
        .Given(() => { });

    "When I find words in this sentence"
        .When(() => output = _wordFinder.FindAllWordsInLowerCase(input));

    string.Format("Then I expect the answer to be [{0}]", string.Join(",", expectedOutput))
        .Then(() => Assert.Equal(expectedOutput, output));
}

What’s nice here is that if you remember the outputs from the InLine tests – it didn’t output the expected string arrays’ content. Here, as the method name that gets generated for the test runner output is simply a string we can manipulate the string to show the contents of the expected array.

all_xunit

Each example provided effectively creates a new scenario, which are grouped in the output by scenarioNumber.StepNumber.

For the output names to be neatly formatted here (without repetition of the input variables) we are setting the attribute [OmitArgumentsFromScenarioNames] on the class, which suppresses them.

Create a dropdown list from an Enum in Asp.Net MVC

A common situation you can find in MVC, is to have an Enum in your model and want that rendered as a dropdown list in the browser.

If you are lucky enough to be using MVC5.1 (or higher), I’d recommend you look at this post that shows how to do this using the new Enum Support that’s part of MVC5.1. If not, then read on.

There are a number of ways of solving this, Currently on StackoverFlow there are 20 answers to this question, but none of them consider what I consider the more elegant solution of using templating.

For anyone not familiar with templating, I’d recommend reading Brad Wilson’s excellent blog on it. The following post is how I like to do it, using Asp.Net MVC with C# and the Razor view engine.

For a model that looks like this

public class PersonViewModel
{
   [Display(Name="First Name")]
   public string FirstName { get; set; }

   [Display(Name = "Last Name")]
   public string LastName { get; set; }

   [Display(Name = "Airline Preference")]
   public AirlinePreference? AirlinePreference { get; set; }
}

With our Enum defined as:

  public enum AirlinePreference
    {
        BritishAirways,
        EasyJet,
        RyanAir,
        Virgin
    }

Wouldn’t it be cool to have view that looks like this

Person Final Page

And for the view to have just one line to render the form content:

@Html.EditorForModel()

This post explains one way of achieving this.

The model should be easy enough to read. The controller action is also, so boring, it’s barely worth mentioning, but here it is:

[HttpGet]
public ActionResult Add()
{
    var viewModel = new PersonViewModel();

    return View(viewModel);
}

Enum Template

Out of the box, MVC will render a textbox if the model type is type string and your View for that element is @Html.EditorFor(x => x.FirstName) (for our model). However, it doesn’t know how to render an Enum, so defaults it to being a textbox. For our model, this would result in a view like this

DefaultEditPage

We need to provide a template for MVC to handle Enums in order for it to render them as a dropdown list. We could provide a template whose name matches our Enum (AirlinePreference) and in that view render our dropdown. We could also supply a UIHint attribute on our model property, to help MVC find our template. I prefer to alter the string template, to look for a property type of Enum and render all Enums in my models by the same piece of code without having to be explicit about it with each Enum in each model. This helps have a consistent way of rendering an Enum the same way on all pages with the least amount of code.

We need to provide the string template, and its important we put it in the right folder. As I want all Enums to use this by default, I’m putting mine here:

/Views/Shared/EditorTemplates/

String.cshtml template

@using MVCProject.Extensions

@{
    var type = Nullable.GetUnderlyingType(ViewData.ModelMetadata.ModelType) ?? ViewData.ModelMetadata.ModelType;

    @(typeof (Enum).IsAssignableFrom(type) ? Html.EnumDropDownListFor(x => x) : Html.TextBoxFor(x => x))
}

The first line gets the type of our property, taking care to cater for a value that may be null. The second line will render a EnumDropDownListFor for an Enum or a TextBoxFor for a (non) Enum, e.g. a string.

EnumDropDownListFor

EnumDropDownListFor is an HtmlHelper extension. Here it is

public static IHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> html, Expression<Func<TModel, TEnum>> expression)
{
    var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

    var enumType = Nullable.GetUnderlyingType(metadata.ModelType) ?? metadata.ModelType;

    var enumValues = Enum.GetValues(enumType).Cast<object>();

    var items = from enumValue in enumValues
                select new SelectListItem
                {
                    Text = enumValue.ToString(),
                    Value = ((int)enumValue).ToString(),
                    Selected = enumValue.Equals(metadata.Model)
                };

    return html.DropDownListFor(expression, items, string.Empty, null);
}

First couple of lines determine the enum type (taking care of the null case too). Then we get an array of Enum values and cast it into a enumerable collection of objects. From here we can easily convert that to an enumerable collection of SelectListItems with a linq query. Once we have the collection of SelectListItems, it’s passed into the DropDownListFor helper to render it as a select list for the client.

In the browser our page now looks like

Person Edit Page, non formattedMuch improved, but not very user friendly to see BrittishAirways, displayed with no space in between.

Format DropDownList Values

We could add a Description Attribute or a Display Attribute to the enum values and update the linq query to display that instead of enumValue.ToString().
However, I’m going to go with a convention over code style approach and use a resx file to store the value of what we want to render to the screen. Also, as we are using a resx file to store the value that get’s displayed to the user we can easily make this support more than one language.
Let’s create a resx file and call it Enums.resx. And add some values in that look like:

EnumsSo, here our convention is the names of the resources follow the pattern
[Enum Name]_[Enum Value]

The final thing to do is update the EnumDropDownFor extension to use these resource values, instead of the enum value.

public static IHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> html, Expression<Func<TModel, TEnum>> expression)
{
    var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

    var enumType = Nullable.GetUnderlyingType(metadata.ModelType) ?? metadata.ModelType;

    var enumValues = Enum.GetValues(enumType).Cast<object>();

    var items = from enumValue in enumValues
                select new SelectListItem
                {
                    Text = GetResourceValueForEnumValue(enumValue),
                    Value = ((int)enumValue).ToString(),
                    Selected = enumValue.Equals(metadata.Model)
                };

    return html.DropDownListFor(expression, items, string.Empty, null);
}

private static string GetResourceValueForEnumValue<TEnum>(TEnum enumValue)
{
    var key = string.Format("{0}_{1}", enumValue.GetType().Name, enumValue);

    return Enums.ResourceManager.GetString(key) ?? enumValue.ToString();
}

We’ve added the static method GetResourceValueForEnumValue, which simply creates a key, based on our convention of [Enum Name]_[Enum Value] and then uses the ResourceManager to lookup the corresponding value from the Enums resource.

Finally, earlier I mentioned that I was using @Html.EditorForModel to create the form in the view. You could also call @Html.EditorFor(x => x.AirlinePreference) if you need to have more control over where to place the dropdown on your form. You can also explicitly call the EnumDropDownListFor of course.

All source code available to download here:
https://github.com/paulthecyclist/EnumBlog