Josh on Los Techiesspacer

I've started posting updates at Joshua Flanagan on the Los Techies community site. If you are subscribed to my feedburner RSS feed, you should already be getting the new content. If not, update your reader to feeds.feedburner.com/JoshuaFlanagan

Wednesday, 10 September 2008 21:21:53 (Central Daylight Time, UTC-05:00) spacer     Comments [0]  | 

 

Sample Opinionated Controllerspacer

In my post on testing opinionated controllers, I mentioned how our controller actions follow a strict convention: one view model object in, one view model object out, and leave the ViewResult creation to the ControllerActionInvoker. Jimmy Bogard asked me to post the controller base class that makes this work. The implementation that we use is too involved to post as a sample, but I was able to create a "pseudo-code" version that demonstrates the ideas. I call it pseudo-code because it is just enough to demonstrate the concept, but it is nowhere near production code. It doesn't account for any edge cases, or really any other case than the single one I tested.

To be clear, neither this, nor the code in my previous post, is straight out of our source tree. It is more "inspired by" as opposed to an accurate representation of our actual code.

The code below shows:

  • OpinionatedController - all of your controllers should derive from this class. Each controller action should take in one view model (DTO) object and return a view model (that optionallyl derives from ResponseViewModel)
  • OpinionatedActionInvoker - does the real work of building the input parameter instance for the controller action, and then creating the default ViewResult from the output. This can very likely cleaned up by taking advantage of the IModelBinder interface in Preview 5 (see ScottGu's post)
  • ResponseViewModel - a base class for action return values for when you want to perform an ActionResult other than ViewResult
public class OpinionatedController : Controller { public OpinionatedController() { ActionInvoker = new OpionatedActionInvoker(); } public class OpionatedActionInvoker : ControllerActionInvoker { protected override object GetParameterValue(ParameterInfo parameterInfo) { var parameterType = parameterInfo.ParameterType; if (parameterType.IsPrimitive) { return getValueFromInput(parameterInfo.Name, parameterType); } var complexActionParameter = Activator.CreateInstance(parameterType); foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(complexActionParameter)) { object propertyValue = getValueFromInput(descriptor.Name, descriptor.PropertyType); if (propertyValue != null) { descriptor.SetValue(complexActionParameter, propertyValue); } } return complexActionParameter; } private object getValueFromInput(string parameterName, Type parameterType) { object propertyValue; ControllerContext.RouteData.Values.TryGetValue(parameterName, out propertyValue); if (propertyValue == null) { propertyValue = ControllerContext.HttpContext.Request.Params[parameterName]; } return TypeDescriptor.GetConverter(parameterType).ConvertFrom(propertyValue); } protected override ActionResult InvokeActionMethod(MethodInfo methodInfo, System.Collections.Generic.IDictionary<string, object> parameters) { object[] actionMethodParams = new object[0]; if (methodInfo.GetParameters().Length > 0) { actionMethodParams = new[] { parameters.Values.ElementAtOrDefault(0) }; } var viewModel = methodInfo.Invoke(ControllerContext.Controller, actionMethodParams); var responseViewModel = viewModel as ResponseViewModel; if (responseViewModel != null && responseViewModel.OverrideResult != null) { return responseViewModel.OverrideResult; } return new ViewResult { ViewData = new ViewDataDictionary { Model = viewModel } }; } } } public class ResponseViewModel { public ActionResult OverrideResult { get; set; } }

I created a solution that I used for testing all of this, to prove it works. It is the adaptation of the codecampserver SpeakerController that I used for my previous post. There is probably a lot of cruft and aborted experiments in there that you can safely ignore. To see it all work, run the website and go the url: /austincodecamp/speakers?displaypage=2&displaypagecount=15 (substituting different values for the code camp name and display page stuff to prove it isn't hardcoded). Download the solution (built with VS2008 and ASP.NET MVC Preview 5)

Tuesday, 02 September 2008 22:07:58 (Central Daylight Time, UTC-05:00) spacer     Comments [0]  | 

 

Testing with Opinionated ASP.NET MVCspacer

Before joining them at Dovetail, I had heard Chad and Jeremy talk over lunch of how they were building their product with Microsoft's ASP.NET MVC. While all of the published literature still left me with questions on how to best handle URLs or test drive controller actions, they spoke of it as a solved problem. They'd say they were using MVC in a very "opinionated" way by hiding or replacing most of the underlying framework. They spoke of "one in, one out" state based testing, invocation registries, yada yada yada, but I couldn't really grasp the impact without seeing the code. Since they haven't yet published any of their work, I figured I'd kick it off with a post about our controller action tests. If you are interested in more details, let us know, and I'm sure one of us can get a few more blog posts out of it. Even better, come talk to us at the Continuous Improvement in .NET Software Development conference.

Have an opinion

The concepts of "opinionated frameworks" and "convention over configuration" have gained a large following from people rejecting the tedious and noisy declarative code typically associated with shaping a one-size-fits-all framework. Regardless of whether you are trudging through XML, or using a fancier programmatic fluent interface, having to repeatedly "state the obvious" in your code will cloud its true intentions and slow you down. Where a one-size-fits-all framework says "we can't make that assumption, because the developer may want to do x, y, or z", the opinionated framework says "we're going to make some assumptions that fit the most common scenarios, and the developer should bend their design to fit those assumptions" (although the good ones will always allow you to deal with edge cases through some sort of overriding mechanism).

I was reminded of this today when reading Phil Haack's post on How a Method Becomes an Action. He made the side remark: "This is one reason you’ve seen the MVC team resistant to including helper methods, such as Url<T>(…), that use an expression to define the URL of an action. The action is not necessarily equivalent to a method on the class with the same name". Its probably a wise move that the Microsoft MVC team is not embedding "opinions" in such a young platform that hasn't had enough real-world usage to drive out the common scenarios. However, within the confines of an individual development team, you can get a lot of value by using the underlying framework in a more opinionated way. By simply deciding, "an action name IS necessarily equivalent to a method name on a controller class", you can safely use a compiler-checked UrlFor<ProductsController>( c=>c.Edit ) helper method throughout your codebase.

Our opinion on controller actions

At Dovetail, we have simplified our development and testing of controller actions by codifying our opinions and conventions. For example:

  • All controller actions have a single input parameter. All of the data provided by the client that is needed by the action is exposed as properties on this view model object.
  • All controller actions return a single view model object that contains all of the data needed to render the view.
  • Specifically, actions do NOT return instances of ActionResult. Since a large majority of actions display a view, we move the ViewResult creation into the ControllerActionInvoker. We include a mechanism for actions to override this behavior and return a specific ActionResult when needed.

By adhering to these conventions, we get a lot of commonality in our tests that we can pull up into a base class. The individual tests themselves become much easier to write, and much clearer to read, in my opinion. As a point of comparison, I'll take an example from codecampserver, an open source application used to teach concepts with ASP.NET MVC. Let's look at the codecampserver tests for the List action on the SpeakerController (see the original at SpeakerControllerTester):

[Test] public void ShouldListSpeakersForAConference() { var p = new Person("joe", "dimaggio", "jd@baseball.com"); var p2 = new Person("marilyn", "monroe", "m@m.com"); _conference.AddSpeaker(p, "joedimaggio", "bio here...", "avatar.jpg"); _conference.AddSpeaker(p2, "marilynmonroe", "bio here...", "avatar.jpg"); using (_mocks.Record()) { SetupResult.For(_conferenceRepository.GetConferenceByKey("austincodecamp2008")) .IgnoreArguments() .Return(_conference); } using (_mocks.Playback()) { SpeakerController controller = createSpeakerController(); var actionResult = controller.List("austinCodeCamp2008", 0, 0) as ViewResult; Assert.That(actionResult, Is.Not.Null); Assert.That(actionResult.ViewName, Is.Null, "expected default view"); var speakersPassedtoView = controller.ViewData.Get<Speaker[]>(); Assert.That(speakersPassedtoView, Is.Not.Null); Assert.That(speakersPassedtoView.Length, Is.EqualTo(2)); } }

Using a test base class built around our conventions we can specify the same behavior with:

[TestFixture] public class When_listing_the_speakers : ControllerActionTest<SpeakerController, SpeakerListRequest, SpeakerListViewModel> { public When_listing_the_speakers() : base((c, input) => c.List(input)) { } protected override void underTheseConditions() { Conference conference = new Conference("austincodecamp2008", "Austin Code Camp"); var p = new Person("joe", "dimaggio", "jd@baseball.com"); var p2 = new Person("marilyn", "monroe", "m@m.com"); conference.AddSpeaker(p, "joedimaggio", "bio here...", "avatar.jpg"); conference.AddSpeaker(p2, "marilynmonroe", "bio here...", "avatar.jpg"); Input = new SpeakerListRequest { ConferenceKey = "austincodecamp2008" }; GivenThat<IConferenceRepository, Conference>(r => r.GetConferenceByKey(null)).IgnoreArguments().Return(conference); } [Test] public void Should_display_the_default_view() { Output.Override.ShouldBeNull(); } [Test] public void Should_provide_the_speakers_related_to_the_conference() { Output.Speakers.ShouldNotBeNull(); Output.Speakers.Length.ShouldEqual(2); } }

As you can see, the assertions are more easily broken out into individual tests. The test methods themselves are very simple, usually consisting of just the assertions on the output. You could also test slightly different scenarios by making changes to the Input property within your test method. I know some people don't like to use a test base class, but I think those objections stem from the idea that they don't want to hide the details of a test. However, in our case, since all of our controller action tests can use the same base class, it can simply be considered part of our test runner infrastructure. Once its functionality is understood for one test, you understand how it works with all of the other tests, unlike if you had to create one-off test base classes for each action or scenario.

In case it isn't clear what is going on here:

  • The controller under test, and the type of the action's input parameter and return value are specified as generic parameters on the ControllerActionTest base class.
  • The method being tested is declared via the lambda passed to the base class's constructor.
  • The controller under test is created in the base class's [SetUp] method using StructureMap's RhinoAutoMocker.
  • The underTheseConditions method is called as part of the [SetUp] method on the base class.
  • Input and Output are properties on the base class that hold an instance of the input parameter and return value types, respectively.
  • GivenThat() is a method on the base class which stubs behavior using Rhino Mocks.
  • The Should*() methods used on the Output properties perform NUnit assertions under the hood, care of Scott Bellware's specification extensions
Monday, 01 September 2008 22:47:35 (Central Daylight Time, UTC-05:00) spacer     Comments [2]  | 

 

Hello StructureMapspacer

In a comment on Derik Whittaker's post about using app.config with StructureMap, Brian Johnston asks for a simple "hello world" example. I figured I could re-create the app that I used when I was first learning StructureMap. This is a very simple demonstration of how to make an application use StructureMap. It does not get into the whys and whens for different scenarios. It is simply a piece of code that will let you see StructureMap "work" so that you can play with it. For more introductory details, I would highly recommend reading Chad Myers' posts that cover Basic and Medium-level usage scenarios for StructureMap.

Create the app

  1. Open Visual Studio
  2. Go to File | New | Console Application (hopefully you have Tools | Options | Projects and Solutions | "Save new projects when created" UNCHECKED so you aren't forced to choose a path for this throwaway code)
  3. Add a reference to StructureMap.dll (agonize over how slow the Add Reference dialog box is to open because it needs to populate the list on the .NET tab which will go unused when we switch to the Browse tab)
  4. Replace the contents of Program.cs with the code at the bottom of this post
  5. Run the application to see the English greeting.

Notes

  • The call to ObjectFactory.GetInstance in the Main method demonstrates how you get an object from StructureMap. Notice that it only specifies the interface for the type you'd like to retrieve so the caller is decoupled from the actual implementation that is used.
  • The ConfigureDependencies method is where I tell StructureMap which concrete implementations I would like to use for my interfaces. You can accomplish this same goal by decorating your interfaces and classes with attributes, or by using a configuration file.
  • Aside from the two bullet items above in the Program class, none of the rest of the code has any reference to or knowledge of StructureMap.
  • Notice that I only ever ask for an IAppEngine. I never had to explicitly ask for an IGreeter or IOutputDisplay. StructureMap was smart enough to recognize these dependencies of AppEngine and injected them automatically.
  • Change the second line in ConfigureDependencies so that TheDefaultIsConcreteType<FrenchGreeter> and run the application again. Notice that you now see the French greeting. You have changed the implementation that is used by AppEngine, without changing any of the application code (if you agree that the Program class is just your launching point, and not really part of the application logic). If you were using a config file to configure StructureMap, you wouldn't have had to change any code at all to see the different behavior.

Code

Update: I've created a gist that shows the original example, but using the cleaner syntax introduced in more recent versions of StructureMap.

The original code from this post that used older StructureMap syntax:

using System; using StructureMap; namespace ConsoleApplication1 { class Program { private static void Main(string[] args) { ConfigureDependencies(); IAppEngine appEngine = ObjectFactory.GetInstance<IAppEngine>(); appEngine.Run(); } private static void ConfigureDependencies() { StructureMapConfiguration.ForRequestedType<IAppEngine>().TheDefaultIsConcreteType<AppEngine>(); StructureMapConfiguration.ForRequestedType<IGreeter>().TheDefaultIsConcreteType<EnglishGreeter>(); StructureMapConfiguration.ForRequestedType<IOutputDisplay>().TheDefaultIsConcreteType<ConsoleOutputDisplay>(); } } public class AppEngine : IAppEngine { private readonly IGreeter greeter; private readonly IOutputDisplay outputDisplay; public AppEngine(IGreeter greeter, IOutputDisplay outputDisplay) { this.greeter = greeter; this.outputDisplay = outputDisplay; } public void Run() { outputDisplay.Show(greeter.GetGreeting()); } } public interface IAppEngine { void Run(); } public interface IGreeter { string GetGreeting(); } public class EnglishGreeter : IGreeter { public string GetGreeting() { return "Hello"; } } public class FrenchGreeter : IGreeter { public string GetGreeting() { return "Bonjour"; } } public interface IOutputDisplay { void Show(string message); } public class ConsoleOutputDisplay : IOutputDisplay { public void Show(string message) { Console.WriteLine(message); } } }
structuremap
Friday, 25 July 2008 09:11:05 (Central Daylight Time, UTC-05:00) spacer     Comments [3]  | 

 

gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.