Monday, June 30, 2008 |
|
|
I have spent the past couple weeks holed up in a cave building a web site using the latest ASP.NET MVC framework. I am behind on blogging, responding to emails, and keeping up with my RSS feeds. I am making good progress with ASP.NET's MVC framework and enjoying the development experience, despite the occasional beta blip. I started developing a site using the MVC Preview 2 source. Two weeks in, I pulled the latest MvcContrib project, and a few days ago I upgraded again to the MVC Preview 3 source. Like anyone using beta software, I have spent some time swearing at my screen, but overall, I am pleased with the MVC framework. After returning from vacation, I now find that I am one more release behind on my MvcContrib binaries. As I began developing with the MVC framework, the similarities I was seeing with Castle's MonoRail MVC were numerous and reassuring. I felt like I could hit the ground running with previous MVC knowledge. The sample MVC application right out of the box is functional and a helpful place to start familiarizing and education oneself with the new framework. For those tools and features that are not built into the framework at this point, I found the MvcContrib project to be helpful in my development. It seemed like with each day of development I would discover something new, cool, or exciting, and I wanted to share it on this blog. As I started down the path of writing a blog entry, I procrastinated each time thinking, "let me just get through this one issue first and then I will blog." That approach snowballed tremendously. With each hurdle I had to surpass, there was another release I had to install. Of course, I did not have to install any of these updates, but I did thinking they would be trivial. In some cases they were, and in some they were not. My point - there is a great deal of change and development constantly occurring on the MVC framework and other tools supporting it. Expect this rate of change while the framework is maturing. Sometimes it is frustrating, but there is great support on the ASP.NET/MVC forums as well as MvcContrib. Why do I like it? MVC promotes a cleaner separation of concerns. Since I am used to developing in ASP.NET and trying to adhere to a loosely defined MVP pattern, this MVC framework mitigates some of the frustrations of having to enforce the pattern as well as SoC. Testability is build into the framework. When creating a new project, there is an option to create a test project to accompany it. I can create a web site quickly and cleanly. As I add a new page (view), I have a distinct controller and view for it. Unlike MVP implemented in ASP.NET, I do not have to enforce my controller-view separation, it is done for me. I am able to use use more than one view engine easily. I am tired of using the ASP.NET view engine. While one can roll their own view engine flavor, MvcContrib provides support for a few out of the box. I can free myself of having to use ASPX pages, ASCX user controls, and their code-behind files. I prefer using the NVelocity view engine. I prefer the more simple HTML view of NVelocity. My customers enjoy it as well, and they do not have to concern themselves with ASP syntax. They will have to learn a few tricks with NVelocity, but nothing that is not intuitive as it is. Extensible source. I have been able to add custom support to provide overrides and additional functionality to the framework as I need it. Community support. The MvcContrib project helps make development with MVC easier. Use their view engines and IoC container support. Know of some functionality that is not available, add it. Pitfalls IIS 6 and URL rewriting. While it is possible to get the nice URL rewriting to work with IIS 6, ISAPI filters, and extension mappings. It is a PITA. I am in the process of upgrading my customer's environment to use IIS 7. Today, their URL contains the ".mvc" string, such as, <domainName>/home.mvc, or <domainName>/account.mvc/login. I suggest having IIS 7 available. Ajax framework. I am still waiting on a good Ajax framework to be included. The one MonoRail includes is nice. As for now, I am using jQuery and developing my own. Hmmm, maybe this functionality could be added to the MvcContrib project, and maybe instead of limiting it to jQuery, I could abstract it to enable other Ajax providers. We'll see what time permits. Third party controls like Telerik, Infragistics, et alia. Rich datagrids, tree controls, and other controls that rely on view state and postbacks. I expect support will come around for these. I tried the NHaml view engine, a .NET implementation of the Rails Haml view engine. My NHaml default layout (master page) was generating runtime errors because I was not use the proper spacing of two characters. Seriously? Two spaces? Who cares. I moved on to try Brail. I have been pleased thus far. Thank you to the ASP.NET MVC team and the contributors to the MvcContrib project. Keep up the great work. |
Monday, June 30, 2008 3:15:53 PM (Mountain Standard Time, UTC-07:00) | |
|
|
|
|
Thursday, May 01, 2008 |
|
|
I have been doing code reviews frequently this past week and have been remarking on the naming conventions of test methods. In a effort to try and find the rules of BDD naming conventions, I came across Dan North's introduction to BDD, which is a great starting point, and a blog post by David Laribee that has made a good impression on me. You can read up on BDD to learn more, this post will not go into detail about it. From what I gathered from David Laribee's article, I like this. I REALLY like this. By changing namespace, class, and method names, the behavior of each spec (test) is clearly revealed. In a larger suite of tests, if there are failures, I can more easily see what is failing and what trends, if any, are resulting in my failures. Before, I would name my test method names to describe a behavior, "Some concept should do this when given that." Getting the namespace and class name involved provides a more legible test result. While reviewing a test case for a peer, I took this approach and arrived at the following scenario. I am changing the names of the methods to hide details. The original test method was "FilterToEndPointTestReport." Making this more readable, using my previous approach, we arrived at "FilterShouldUpdateStatusOnReportWhenDefaultFilterValueIsChanged." Great, that is better, but... Applying the approach described in David's post, I now have this. 1: namespace Specs_for_Filters 2: { 3: public class When_default_filter_value_is_changed : FilterTestFixture 4: { 5: [Test] 6: public void Endpoint_should_be_updated_on_report {} 7: 8: [Test] 9: public void Endpoint_should_be_updated_on_grid {} 10: 11: [Test] 12: public void Endpoint_should_be_updated_on_chart {} 13: } 14: 15: public abstract class FilterTestFixture 16: { 17: // provide common functionality that will be used 18: // among behavior-driven filter test classes. 19: } 20: }
When my tests run, their resulting output is more legible. This approach helps me to think about my tests in terms of behavior rather than implementation. In my opinion, my test cases are now more abstract and more succinct. |
Thursday, May 01, 2008 11:48:23 AM (Mountain Standard Time, UTC-07:00) | | Design
|
|
|
|
Tuesday, April 29, 2008 |
|
|
I am undecided on Twitter. The jury is still out in my mind. On any given day, there are things I need to do and things I would like to do. Not taking into account family things, I read through my emails, both work and personal, read through my RSS feeds, news feeds, weather reports, IM with close friends and family, read a few credible news sources, and somewhere find time to work and research new and existing technologies. If I can get that far, that is a busy day, and that does not include family. So where am I supposed to find time to update everyone on Twitter? How is Twitter fitting into your schedule? You can follow me, but I am not really all that active, at least until I find more time. |
Tuesday, April 29, 2008 10:17:29 PM (Mountain Standard Time, UTC-07:00) | |
|
|
|
|
Thursday, April 17, 2008 |
|
|
I was in a design discussion today and mentioned the need for a singleton object in our solution. Further along in the discussion I was trying to find a word that describes the opposite of a singleton, but alas, I struggled to find a word that may or may not exist. I just referred to the inverse object as a "new object." As I pondered this later in the day, I considered adjectives describing the nature of objects. They can be many things, but two concepts that popped into my head to classify them were "complex" and "simple." I started playing with the words and making them resemble singleton. "Complexton" did not sound right to me. I should mention that I was washing my hands as I was contemplating this. I lifted up my head, looked into the mirror, and thought, "SIMPLETON." The opposite of a singleton is a "simpleton." If the opposite of a singleton is not a "simpleton," then what is it? Until I find a plausible answer, I will try and insert this new term into my next design discussion. |
Thursday, April 17, 2008 8:41:34 PM (Mountain Standard Time, UTC-07:00) | | Design | Technology
|
|
|
|
Tuesday, April 08, 2008 |
|
|
Duplicate code to me is wrong. Writing duplicate code to me is like using poor grammar. If I am unaware of it, I am none the wiser. However, if I knowingly use poor grammar or duplicate code, I feel bad.
After seeing too many duplications across methods in one or more classes, I decided to investigate a way to remove these. I am always looking to remove duplicate code, even code that shares similarities, I look to refator. Removing duplications is important to adapt more easily to change. When a code change is required, we should only have to make it in one place. The following article will show two relatively simple means to address this code smell, delegates and an Aspect-Oriented approach.
The example I am using in this article is seen below. It is a unit test class, StackFixture class, and it is extremely trivial. This is a typical unit test taken from Test-Driven Development in Microsoft .NET. I have added the logging functionality as an easy means to show how these types of DRY violations can occur. While the duplications in this example deal with logging, they could pertain to other, more complex, functionality as well. The approaches to removing duplications across methods in this article can work with these simple scenarios, as well as more complex ones.
Take for example this sample test case. Each test method logs a message before and after executing the test logic. This violates DRY. We want to keep our test logic in our method, but factor out the duplicate logging. Again, if we were not logging, but doing some repetitive logic, we could factor that out as well.
1: [TestFixture] 2: public class StackFixture : AbstractBaseFixture 3: { 4: private Stack stack; 5: 6: [SetUp] 7: public void SetUp() 8: { 9: stack = new Stack(); 10: } 11: 12: [Test] 13: public void Empty() 14: { 15: Log.Info("Starting Empty test"); 16: 17: Assert.IsTrue(stack.IsEmpty); 18: 19: Log.Info("Ending Empty test"); 20: } 21: 22: [Test] 23: public void PushOne() 24: { 25: Log.Info("Starting PushOne test"); 26: 27: stack.Push("first element"); 28: Assert.IsFalse(stack.IsEmpty, "After Push, IsEmpty should be false."); 29: 30: Log.Info("Ending PushOne test"); 31: } 32: 33: [Test] 34: public void Pop() 35: { 36: Log.Info("Starting Pop test"); 37: 38: stack.Push("first element"); 39: stack.Pop(); 40: Assert.IsTrue(stack.IsEmpty, "After Push - Pop, IsEmpty should be true."); 41: 42: Log.Info("Ending Pop test"); 43: } 44: 45: [Test] 46: [ExpectedException(typeof(InvalidOperationException))] 47: public void PopEmptyStack() 48: { 49: Log.Info("Starting PopEmptyStack test"); 50: 51: stack.Pop(); 52: 53: Log.Info("Ending PopEmptyStack test"); 54: } 55: }
Remove Duplications with Delegates
Quite simply, we can remove our method duplications using a delegate, in this case, the System.Action delegate. Thanks to Ayende for helping me understand this option. We create a method, TestMethod (as shown below), and add it to our StackFixture class. The method takes two strings (string beforeMessage and string afterMessage) and the Action delegate, representing the test method logic. We will call this method within our test cases.
1: public void TestMethod(string beforeMessage, string afterMessage, Action action) 2: { 3: // before 4: Log.Info(beforeMessage); 5: 6: // invoke our method 7: action(); 8: 9: // after 10: Log.Info(afterMessage); 11: }
Then, in our tests, we replace the following test method...
1: [Test] 2: public void Pop() 3: { 4: Log.Info("Starting Pop test"); 5: 6: stack.Push("first element"); 7: stack.Pop(); 8: Assert.IsTrue(stack.IsEmpty, "After Push - Pop, IsEmpty should be true."); 9: 10: Log.Info("Ending Pop test"); 11: }
With the same method using our delegate approach.
1: [Test] 2: public void Pop() 3: { 4: TestMethod("Starting Pop test", "Ending Pop test", 5: delegate 6: { 7: stack.Push("first element"); 8: stack.Pop(); 9: Assert.IsTrue(stack.IsEmpty, "After Push - Pop, IsEmpty should be true."); 10: }); 11: }
We can replace each of our test methods with the same TestMethod using the System.Action delegate. It will then easy to omit our before and after strings replacing them with "action.Method.Name," or some other intelligent logic to determine what to log.
This approach works well, but delegates are often difficult to understand. If this solution suits your needs, make sure the implementation is easily maintainable. What is nice about this approach is how simple it is. There is no need to reference any assemblies outside of the .Net framework, i.e. no third party dependencies.
The Aspect-Oriented Approach
Aspect-Oriented Programming (AOP) is an entirely different animal. It certainly deserves more than just a blog post. Please read more about it online. This article will not explain the details of AOP.
If you are reading this, welcome back. I will assume you are now familiar with AOP. There are several options for AOP frameworks. Some frameworks I have used are Spring.net, Castle Project, and PostSharp. The former two provide IoC capabilities as well. I suggest using a well-supported framework, one with community support and frequent updates. Investigate for yourself, there are several nice options available.
AOP with Castle DynamicProxy
Hamilton Verissimo put together a good sample using Castle's DynamicProxy. It is a nice, clean implementation.This worked fine for me and would work well in other situations. However, in my scenario, if I were to use this approach, I needed to have each of my test classes extend from MarshalByRef. In addition, I would need to modify the underlying NUnit framework to create my proxies in order to provide advice. Since I could not do the latter, or did not want to investigate, I searched for other AOP options.
The DynamicProxy aproach to solving your AOP needs is a great option. It just did not work in my situation, only because NUnit executes each of my methods. I would need to somehow intercept the creation of my test class, create a proxy of it, and execute the test methods on it.
AOP with PostSharp
Gael Fraiteur's PostSharp is a great option for AOP needs. It is extremely clean and easy to use. It is the simplest AOP framework I have used. I suggest watching Gael's video tutorial.
The first thing I needed to do, besides downloading and installing PostSharp, is to add two references to my project, PostSharp.Laos and PostSharp.Public. After that, I create a simple class extending OnMethodInvocationAspect, called LoggingMethodInvocationAspect.
1: [Serializable] 2: public sealed class LoggingMethodInvocationAspect : OnMethodInvocationAspect 3: { 4: private ILogger log; 5: 6: public LoggingMethodInvocationAspect(ILogger logger) 7: { 8: log = logger; 9: { 10: 11: public override void OnInvocation(MethodInvocationEventArgs eventArgs) 12: { 13: // before 14: log.Info("OnInvocation Before proceed"); 15: 16: // invoke 17: eventArgs.Proceed(); 18: 19: // after 20: log.Info("OnInvocation After proceed"); 21: } 22: }
At this point, all we need to do is apply our aspect on target methods. The "AttributeTargetMembers" attribute can use wildcards. This tells the AOP framework to only intercept those methods in the "MathService" class that start with "Test." Below is the sample where I specify the target assembly, target type (class or classes to intercept), and target methods to intercept. There are many features beyond what I am showing so do further investigation.
1: [assembly: ClassLibrary.SampleInterfaces.LoggingMethodInvocationAspect( 2: AttributeTargetAssemblies = "ClassLibrary.UnitTesting.Sandbox", 3: AttributeTargetTypes = "ClassLibrary.UnitTesting.Sandbox.MathService", 4: AttributeTargetMembers = "Test*")]
Finally, my StackFixture class now looks something like this. Notice that the duplicate logging logic is now removed and each test method is prefixed with "Test" in the method name. This is so that PostSharp can filter what methods to intercept.
1: [assembly: ClassLibrary.SampleInterfaces.LoggingMethodInvocationAspect( 2: AttributeTargetAssemblies = "ClassLibrary.UnitTesting.Sandbox", 3: AttributeTargetTypes = "ClassLibrary.UnitTesting.Sandbox.MathService", 4: AttributeTargetMembers = "Test*")] 5: 6: [TestFixture] 7: public class StackFixture 8: { 9: private Stack stack; 10: 11: [SetUp] 12: public void SetUp() 13: { 14: stack = new Stack(); 15: } 16: 17: [TearDown] 18: public void TearDown(){} 19: 20: [Test] 21: public void TestEmpty() 22: { 23: Assert.IsTrue(stack.IsEmpty); 24: } 25: 26: [Test] 27: public void TestPushOne() 28: { 29: stack.Push("first element"); 30: Assert.IsFalse(stack.IsEmpty, "After Push, IsEmpty should be false."); 31: } 32: 33: [Test] 34: public void TestPop() 35: { 36: stack.Push("first element"); 37: stack.Pop(); 38: Assert.IsTrue(stack.IsEmpty, "After Push - Pop, IsEmpty should be true."); 39: } 40: 41: [Test] 42: [ExpectedException(typeof(InvalidOperationException))] 43: public void TestPopEmptyStack() 44: { 45: stack.Pop(); 46: } 47: }
The end result, a StackFixture test class with duplicate logging logic removed. The PostSharp AOP framework post-processes the compiled assembly and inserts itself, easily providing points of interception.
The PostSharp approach does involve a third-party dependency, but I feel like it is cleaner than the delegate approach. Gael informed me future versions of PostSharp will provide a more configurable solution than adding the [assembly ...] reference for the aspects, perhaps an XML configuration option. This can be done today, but involves some work.
Whether you use delegates, AOP, or some other approach, remove duplicate code wherever possible. I am happy to use either approach in my projects. There are tradeoffs to either solution, so investigate for yourself. |
|
|
|
|
| |