Dependency Management in .Net: Get

On September 21, 2011, in Uncategorized, by derekgreer

[Update: This article refers to a tool which will no longer be maintained. Until such time as NuGet is updated to naively support these capabilities, consider using the plug-in described here.]

In my last article, I demonstrated how my team is currently using NuGet.exe from our rake build to facilitate application-level, build-time retrieval of external dependencies.  Since not everyone uses rake for their build process, I decided to create a simple tool that could be consumed by any build process.

To see how the tool works, follow these steps:

Step 1: From the command line, execute the following:

$> nuget install Get

Step 2: Change directory to the Get tools folder.


Step 3: Create a plain text file named dependencies.config and add the following package references:

NHibernate   3.2.0.4000
Moq          4.0.10827

Step 4: Execute the following command:


$> get dependencies.config

The tool currently supports NuGet’s -Source, -ExcludeVersion, and -OutputDirectory switches.  From here, you just need to have it download to a central lib folder and adjust your project references as necessary.  Now stop checking in those assemblies! :)

Tagged with:  

In my last article, I discussed some of my previous experiences with dependency management solutions and set forth some primary objectives I believe a dependency management tool should facilitate. In this article, I’ll show how I’m currently leveraging NuGet’s command line tool to help facilitate my dependency management goals.

First, it should be noted that NuGet was designed primarily to help .Net developers more easily discover, add, update, and remove dependencies to externally managed packages from within Visual Studio. It was not designed to support build-time, application-level dependency management outside of Visual Studio. While NuGet wasn’t designed for this purpose, I believe it currently represents the best available option for accomplishing these goals.

Approach 1

My team and I first started using NuGet for retrieving application dependencies at build-time a few months after its initial release, though we’ve evolved our strategy a bit over time. Our first approach used a batch file we named install-packages.bat that used NuGet.exe to process a single packages.config file located in the root of our source folder and download the dependencies into a standard \lib folder. We would then run the batch file after adding any new dependencies to the packages.config and proceed to make assembly references as normal from Visual Studio. We also use Mercurial as our VCS and added a rule to our .hgignore file to keep from checking in the downloaded assemblies. To ensure a freshly downloaded solution obtained all of its needed dependencies, we just added a call to our batch file from a Pre-build event in one of our project files. Voilà!

Here’s an example of our single packages.config file (note, it’s just a regular NuGet config file which it normally stores in the project folder):

<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Antlr" version="3.1.3.42154" />
<package id="Castle.Core" version="2.5.1" />
<package id="Iesi.Collections" version="3.2.0.4000" />
<package id="NHibernate" version="3.2.0.4000" />
<package id="FluentNHibernate" version="1.3.0.717" />
<package id="Machine.Specifications" version="0.4.9.0" />
<package id="Machine.Fakes" version="0.2.1.2" />
<package id="Machine.Fakes.Moq" version="0.2.1.2" />
<package id="Moq" version="4.0.10827" />
<package id="Moq.Contrib" version="0.3" />
<package id="SeleniumDotNet-2.0RC" version="3.0.0.0" />
<package id="AutoMapper" version="1.1.0.118" />
<package id="Autofac" version="2.4.5.724" />
<package id="Autofac.Mvc3" version="2.4.5.724" />
<package id="Autofac.Web" version="2.4.5.724" />
<package id="CassiniDev" version="4.0.1.7" />
<package id="NDesk.Options" version="0.2.1" />
<package id="log4net" version="1.2.10" />
<package id="MvcContrib.Mvc3.TestHelper-ci" version="3.0.60.0" />
<package id="NHibernateProfiler" version="1.0.0.838" />
<package id="SquishIt" version="0.7.1" />
<package id="AjaxMin" version="4.13.4076.28499" />
<package id="ExpectedObjects" version="1.0.0.0" />
<package id="RazorEngine" version="2.1" />
<package id="FluentMigrator" version="0.9.1.0" />
<package id="Firefox" version="3.6.6" />

 

Here’s the batch file we used:

@echo off
set SCRIPT_DIR=%~dp0
set NUGET=%SCRIPT_DIR%..\tools\NuGet\NuGet.exe
set PACKAGES=%SCRIPT_DIR%..\src\packages.config
set DESTINATION=%SCRIPT_DIR%..\lib\
set LOCALCACHE=C:\Packages\
set CORPCACHE=//corpShare/Packages/
set DEFAULT_FEED="https://go.microsoft.com/fwlink/?LinkID=206669"

echo [Installing NuGet Packages]
if NOT EXIST %DESTINATION% mkdir %DESTINATION%

echo.
echo [Installing From Local Machine Cache]
%NUGET% install %PACKAGES% -o %DESTINATION% -Source %LOCALCACHE%

echo.
echo [Installing From Corporate Cache]
%NUGET% install %PACKAGES% -o %DESTINATION% -Source %CORPCACHE%

echo.
echo [Installing From Internet]
%NUGET% install %PACKAGES% -o %DESTINATION%

echo.
echo [Copying To Local Machine Cache]
xcopy /y /d /s %DESTINATION%*.nupkg %LOCALCACHE%

echo.
echo Done

 

This batch file uses NuGet to retrieve dependencies first from a local cache, then from a corporate level cache, then from the default NuGet feed. It then copies any of the newly retrieved packages to the local cache.  I don’t remember if NuGet had caching when this was first written, but it was decided to keep our own local cache due to the fact that NuGet only seemed to cache packages if retrieved from the default feed. We used the corporate cache as a sort of poor-man’s private repository for things we didn’t want to push up to the public feed.

The main drawback to this approach was that we had to keep up with all of the transitive dependencies. When specifying a packages.config file, NuGet.exe only retrieves the packages listed in the file. It doesn’t retrieve any of the dependencies of the packages listed in the file.

Approach 2

In an attempt to improve upon this approach, we moved the execution of NuGet.exe into our rake build. In doing so, we were able to eliminate the need to specify transitive dependencies by ditching the use of the packages.config file in favor of a Ruby dictionary. We also removed the Pre-Build rule in favor of just running rake prior to building in Visual Studio.

Here is our dictionary which we store in a packages.rb file:

packages = [
[ "FluentNHibernate",              "1.3.0.717" ],
[ "Machine.Specifications",        "0.4.9.0" ],
[ "Moq",                           "4.0.10827" ],
[ "Moq.Contrib",                   "0.3" ],
[ "Selenium.WebDriver",            "2.5.1" ],
[ "Selenium.Support",              "2.5.1" ],
[ "AutoMapper",                    "1.1.0.118" ],
[ "Autofac",                       "2.4.5.724" ],
[ "Autofac.Mvc3",                  "2.4.5.724" ],
[ "Autofac.Web",                   "2.4.5.724" ],
[ "NDesk.Options",                 "0.2.1" ],
[ "MvcContrib.Mvc3.TestHelper-ci", "3.0.60.0" ],
[ "NHibernateProfiler",            "1.0.0.912" ],
[ "SquishIt",                      "0.7.1" ],
[ "ExpectedObjects",               "1.0.0.0" ],
[ "RazorEngine",                   "2.1"],
[ "FluentMigrator",                "0.9.1.0"],
[ "Firefox",                       "3.6.6"],
[ "FluentValidation",              "3.1.0.0" ],
[ "log4net",                       "1.2.10" ]
]

configatron.packages = packages

 

Here’s the pertinent sections of our rakefile:
require 'rubygems'
require 'configatron'

...

FEEDS = ["//corpShare/Packages/", "https://go.microsoft.com/fwlink/?LinkID=206669" ]

require './packages.rb'

task :default => ["build:all"]

namespace :build do

	task :all => [:clean, :dependencies, :compile, :specs, :package]	

	...


	task :dependencies do
		configatron.packages.each do | package |
			FEEDS.each do | feed | 
				!(File.exists?("#{LIB_PATH}/#{package[0]}")) and
					sh "#{TOOLS_PATH}/NuGet/nuget Install #{package[0]} -Version #{package[1]} -o #{LIB_PATH} -Source #{feed} -ExcludeVersion" do | cmd, results | cmd  end
			end
		end
	end

end

 

Another change we made was to use the -ExcludeVersion switch to enable us to setup up the Visual Studio references one time without having to change them every time we upgrade versions. Ideally, I’d like to avoid having to reference transitive dependencies altogether, but I haven’t come up with a clean way of doing this yet.

Approach 2: Update

As of version 1.4, NuGet will now resolve a package’s dependencies (i.e. transitive dependencies) from any of the provided sources (see workitem 603). This allows us to modify the above script to issue a single call to nuget:

    task :dependencies do
        configatron.packages.each do | package |
            !(File.exists?("#{LIB_PATH}/#{package[0]}")) and
                    feeds = FEEDS.map {|x|"-Source " + x }.join(' ')
                    sh "nuget Install #{package[0]} -Version #{package[1]} -o #{LIB_PATH} #{feeds} -ExcludeVersion" do | cmd, results | cmd  end
        end
    end

Summary

While NuGet wasn’t designed to support build-time, application-level dependency management outside of Visual Studio in the way demonstrated here, it suits my team’s needs for now. My hope is NuGet will eventually support these scenarios more directly.

Tagged with:  

Dependency Management in .Net

On September 18, 2011, in Uncategorized, by derekgreer

I started my career as a programmer developing on Unix platforms, primarily writing applications in ANSI C and C++.  Due to a number of factors, including the platform dependency of C/C++ libraries, the low-level nature of the language and the immaturity of the Internet, code reuse in the form of reusable libraries wasn’t as prevalent as it is today.  Most of the projects I developed back then didn’t have a lot of external dependencies and the code I reused across projects was checked out and compiled locally as part of my build process.  Then came Java.

When I first started developing in Java, I remember being excited over the level of community surrounding the platform.  The Java platform inspired numerous open source projects, due both to the platform’s architecture and the increasing popularity of the Internet.  The Apache Jakarta Project in particular was a repository for many of the most popular frameworks at the time.  The increase in the use of open source libraries during this time, along with some conditioning from the past, help forge a new approach to dependency management.

The Unix development community had long since established best practices around the use of source control and one of the practices long discouraged was that of checking in binaries and assets generated by your project.  Helping facilitate this practice was Apache’s Ant framework, an XML-based Java build library.  One of the targets provided by Ant was <get> which allowed for the retrieval of files over HTTP.  A typical scenario was to set up an internal site which hosted all the versioned libraries shared by an organization and to use Ant build files to download the libraries locally (if not already present) when compiling the application.  The task used for retrieving the dependencies effectively became the manifest for what was required to reproduce the build represented by a particular version of an application.  The shortcoming of this approach, however, was the lack of standards around setting up distribution repositories and dealing with caching.  Enter Maven.  Maven was a 2nd generation Java build framework which standardized the dependency management process.  Among other things, Maven introduced schema for denoting project dependencies, local caching and recommendations around repository setup and versioning conventions.

After developing on the Java platform for several years, I landed in a group which decided to rewrite the project I was assigned to from Java to .Net.  After some reorganization, I found myself working alongside new team members whose background was primarily in Microsoft based technologies.  I soon discovered that the typical practice within the Microsoft community was to check in any dependencies needed by a project.  This certainly added a level of convenience for getting projects set up, but no strategy existed for effectively managing versioned distributions of common libraries or easily discovering what versions of what dependencies a project used.

Around this time, Microsoft released beta 2 of the .Net framework and my team decided to upgrade our fledgling project to the new version.  Along with the 2.0 version of the framework came MSBuild, Microsoft’s new build engine.  While a port of Ant was available for the .Net framework at the time, my team decided to go with MSBuild since Visual Studio used it as its underlying build solution.  Unfortunately, MSBuild didn’t provide tasks for downloading dependencies, so I set out to write my own set of tasks which allowed us to manage dependencies “Maven-style”.  While these new tasks provided the desired capability, the strategy proved to be too foreign a concept for the rest of my team resulting in a return to just checking in all dependencies.  Several years later I made another attempt at introducing dependency management to a different .Net team, this time using NAnt, though I believe the group decided to return to using MSBuild and checking everything in again after I left the company.

Around mid-2010, I heard that a new .Net package management system was in the works named “OpenWrap”.  It wasn’t ready at the time, but I was excited that the community seemed to be moving in the right direction.  Not too long after the announcement of OpenWrap came an announcement from Microsoft that they had joined forces with an existing .Net package management project called Nubular (Nu).  The Nu project was a command line .Net package management system built upon RubyGems.  Nu was rewritten to remove the Ruby dependency and re-branded as NuPack which was shortly thereafter re-branded as NuGet.

NuGet was first released in January of 2011 and seems to have been well-received by many in the .Net community.  It’s reception is likely due to the fact that it was designed to accommodate how the majority of .Net developers were already working.   Primarily designed as a Visual Studio extension, NuGet adds a new menu item under the project ‘References’ context-sensitive menu for referencing packages along with adding a Package Manager Console for integrating PowerShell usage and (as of version 1.4) a Package Visualizer which provides various graphical diagrams for visualizing dependencies.  The NuGet team also provides a separate command-line utility (NuGet.exe) which adds the ability to create and publish your own packages.

The availability of a good .Net dependency management tool has been long overdue and NuGet addresses this need in a way palatable to most .Net development teams.  That said, there are some dependency management scenarios I wish the NuGet team had put more emphasis on, namely build-time retrieval of dependencies and application level management independent of Visual Studio integration.

NuGet works a little differently than the other approaches I’ve used in the past in that it’s primary focus isn’t to facilitate the build-time retrieval of dependencies, but rather to make it easy to add, update, and remove project references to external libraries from within Visual Studio.  When using NuGet, it’s expected that you’ll still be checking in any dependencies you reference by your project (though solutions have been set forth to facilitate source-only commits).  While the NuGet.exe command line tool  can be used to facilitate a more traditional approach to dependency management, the NuGet team’s focus on Visual Studio integration imposes some limitation on what can be done without a bit of supplemental infrastructure and perhaps a bit of compromise.

While I appreciate the value offered through NuGet’s Visual Studio integration (without which the tool may have suffered in its reception), I would have preferred the team had started with the following key scenarios:

  1. Provide a command line tool to Retrieve, Update and Remove assets along with transitive dependencies independent of coupling with Visual Studio.

  2. Use a single, plain-text manifest file for listing dependencies to retrieve.

  3. Allow transitive dependencies to be retrieved from any specified source

  4. Provide options for extracting to versioned or non-versioned destination folders as well as a single target destination folder (e.g. “lib”).

The support of these scenarios would certainly have influenced the evolution of NuGet’s Visual Studio integration, but while the underlying implementation may have differed, I believe a similar user experience could still have been implemented.

In my next article, I’ll show how my team is currently leveraging NuGet’s command line tool to facilitate dependency management needs apart from the tool’s Visual Studio integration.  Stay tuned!

Tagged with:  

Effective Tests: Acceptance Tests

On September 5, 2011, in Uncategorized, by derekgreer
This entry is part 17 of 17 in the series Effective Tests

In the last installment of our series, we discussed the topic of Context Obscurity along with strategies for avoiding the creation of obscure tests. As the final topic of this series, we’ll take an introductory look at the practice of writing Automated Acceptance Tests.

Acceptance Tests

Acceptance testing is the process of validating the behavior of a system from end-to-end. That is to say, acceptance tests ask the question: “When all the pieces are put together, does it work?” Often, when components are designed in isolation at the component or unit level, issues are discovered at the point those components are integrated together. Regression in system behavior can also occur after an initial successful integration of the system due to on-going development without the protection of an end-to-end regression test suite.

Automated acceptance testing is accomplished by scripting interactions with a system along with verification of an observable outcome. For Graphical User Interface applications, acceptance tests typically employ the use of UI-automated testing tools. Examples include Selenium, Watir and WatiN for testing Web applications; ThoughtWorks’ Project White for testing Windows desktop applications; and Window Licker for Java Swing-based applications.

The authorship of acceptance tests fall into two general categories: collaborative and non-collaborative. Collaborative acceptance tests use tools which separate a testing grammar from the test implementation, allowing non-technical team members to collaborate on the writing of acceptance tests. Tools used for authoring collaborative acceptance tests include FitNesse, Cucumber, and StoryTeller. Non-collaborative acceptance tests combine the grammar and implementation of the tests, are typically written in the same language as the application being tested and can be authored using traditional xUnit testing frameworks.

Acceptance Test Driven Development

Acceptance Test Driven Development (A-TDD) is a software development process in which the features of an application are developed incrementally to satisfy specifications expressed in the form of automated acceptance tests where the feature implementation phase is development using the Test-Driven Development methodology (i.e. Red/Green/Refactor).

The relationship of A-TDD to TDD can be expressed as two concentric processes where the outer process represents the authoring of automated acceptance tests which serve as the catalyst for an inner process which represents the implementation of features developed following the TDD methodology. The following diagram depicts this relationship:

 

ATDD

 

When following the A-TDD methodology, one or more acceptance tests are written to reflect the acceptance criteria of a given user story. Based upon the expectations of the acceptance tests, one or more components are implemented using the TDD methodology until the acceptance tests pass. This process is depicted in the following diagram:

 

TDD-Process

 

An Example

The following is a simple example of an automated acceptance test for testing a feature to be developed for a Web commerce application. This test will validate that the first five products in the system are displayed to the user upon visiting the landing page for the site. The tools we’ll be using to implement our acceptance test are the Machine.Specifications library (a.k.a. MSpec) and the Selenium Web UI testing library.

A Few Recommendations

When testing .Net Web applications on a Windows platform, the following are a few recommendations you may want to consider for establishing your acceptance testing infrastructure:

  • Use IIS Express or a portable Web server such as CassiniDev.
  • Use a portable instance of Firefox or other targeted browser supported by your selected UI automated testing library.
  • Establish testing infrastructure which allows single instances of time-consuming processes to be started once for all acceptance tests. Examples of such processes would include starting up Web server and/or browser processes and configuring any Object-Relational Mapping components (e.g. NHibernate SessionFactory initialization).
  • Establish testing infrastructure which performs a complete setup and tear down of the application database. Consider setting up multiple strategies for running full test suites vs. running the current use case under test.

 

To keep our example simple, we’ll assume our web site is already deployed as the default site on localhost port 80, that the database utilized by the application is already installed and configured, that our current machine has Firefox installed and that each test will be responsible for launching an instance of Selenium’s FirefoxDriver.

Here’s our acceptance test:

    [Subject("List Products")]
    public class when_a_user_requests_the_default_view
    {
        static Database _database;
        static FirefoxDriver _driver;

        Establish context = () =>
            {
                // Establish known database state
                _database = _database = new Database().Open();
                _database.RemoveAllProducts();
                Enumerable.Range(0, 10).ForEach(i => _database.AddProduct(i));
                _database.Close();

                // Start the browser
                _driver = new FirefoxDriver();
            };

        Cleanup after = () => _driver.Close();

        Because of = () => _driver.Navigate().GoToUrl("http://localhost/");

        It should_display_the_first_five_products = () => _driver.FindElements(By.ClassName("customer")).Count().ShouldEqual(5);
    }

In this test, the Establish delegate is used to set up the expectations for this scenario (i.e. the context). Those expectations include the presence of at least five product records in the database and that a Firefox browser is ready to use. The Because delete is used to initiate our singe action which should trigger our expected outcome. The It delegate is then used to verify that a call to the Selenium WebDriver’s FindElements() method returns exactly five elements with a class of ‘customer’. Upon completion of the test, the Cleanup delegate is used to close the browser.

Conclusion

The intent of this article was merely to introduce the topic of automated acceptance testing since a thorough treatment of the subject is beyond the scope of this series. For further reading on this topic, I highly recommend the book Growing Object-Oriented Software Guided By Tests by Steve Freeman and Nat Pryce. While written from a Java perspective, this book is an excellent guide to Acceptance Test Driven Development and test writing practices in general.

This article concludes the Effective Tests series.  I hope it will serve as a useful resource for the software development community.

Tagged with:  

Effective Tests: Avoiding Context Obscurity

On July 19, 2011, in Uncategorized, by derekgreer
This entry is part 16 of 17 in the series Effective Tests

In the last installment of our series, we looked at the Expected Object pattern as a way to reduce code duplication, eliminate the need to add test-specific equality concerns to production code and to aid in clarifying the intent of our tests. This time we’ll take a look at some practices and techniques for avoiding context obscurity.

Context Obscurity

Validating the behavior of a system generally requires instantiating the System Under Test along with the setup of various dependencies and/or parameter objects which serve to define the context for the system’s execution. When the essential context is not discernible from the test implementation, this results in Context Obscurity.

Context Obscurity Causes

The following sections list some of the main causes of context obscurity.

Incidental Context

The setup needs for a test often includes information necessary for the behavior’s execution, yet irrelevant to the behavior being validated. For example, a given component may require a logging dependency to be supplied for instantiation, but the behavior being tested may have nothing to do with whether the component logs information or not. These type of setup concerns lead to Incidental Context which can affect the clarity of the test.

Consider the following specification for a payment gateway component which validates that an exception is thrown when the system is asked to process a payment containing an expired credit card:

public class when_processing_a_payment_with_an_expired_credit_card
{
	static Exception _exception;
	static PaymentGateway _paymentGateway;
	static PaymentInformation _paymentInformation;
	static Mock<ILoggger> _nullLogger;
	static Mock<IPaymentProvider> _stubPaymentProvider;

	Establish context = () =>
		{
			_nullLogger = new Mock<ILoggger>();
			_stubPaymentProvider = new Mock<IPaymentProvider>();
			_stubPaymentProvider.Setup(x => x.ProcessPayment(Parameter.IsAny<Payment>()))
				.Returns(new PaymentReceipt
					        {
					         	ReceiptId = "12345",
						ChargeAmount = 300.00m,
						CardType = "Visa",
						CardLastFour = "1111",
						VenderId = "FF26AA123"

					        });
			_paymentGateway = new PaymentGateway(_stubPaymentProvider.Object, _nullLogger.Object);

			_paymentInformation = new PaymentInformation
				                      {
				                      	Amount = 300.00m,
				                      	CreditCardNumber = "41111111111111",
				                      	CreditCardType = CardType.Visa,
				                      	ExpirationDate = Date.Today.Subtract(TimeSpan.FromDays(1)),
				                      	CardHolderName = "John P. Doe",
				                      	BillingAddress = new Address
				                      		                {
				                      		                 	Name = "John Doe",
				                      		                 	AddressLineOne = "123 Street",
				                      		                 	City = "Springfield",
				                      		                 	StateCode = "MO",
				                      		                 	Zipcode = "65807"
				                      		                }
				                      };
		};

	Because of = () => _exception = Catch.Exception(() => _paymentGateway.Process(_paymentInformation));

	It should_throw_an_expired_card_exception = () => _exception.ShouldBeOfType<ExpiredCardException>();
}
Listing 1

While the specification in listing 1 only validates that an error occurs when an expired credit card is provided, there is quite a bit of set up necessary for the specification’s execution. Since the only information relevant to understanding how the system fulfills this behavior is how the PaymentGateway type is called, the value of the expiration date and the expected result, the majority of the setup is incidental to the modeling of the specification leading to to a low signal to noise ratio.

Lack of Cohesion

Another source of incidental context can occur when a System Under Test lacks Cohesion. Cohesion can be defined as the functional relatedness of a module. When a module serving as the System Under Test lacks cohesion (i.e. has multiple unrelated responsibilities), this can result in the need to setup dependencies which are never used by the area of concern being exercised by the test.

Consider the following specification which verifies that related product information is returned when a customer requests the details for a product:

public class when_the_customer_requests_product_information
{
	static readonly Guid ProductId = new Guid("BD1F1F9A-85BC-48B9-95B5-0CA8219A97A1");
	static readonly Guid RelatedProductId = new Guid("C363577B-1720-43C1-93D9-2C9F239B3D52");
	static Mock<IAuditService> _auditServiceStub;
	static Mock<IOrderHistoryRepository> _orderHistoryRepositoryStub;
	static Mock<IOrderReturnService> _orderReturnServiceStub;
	static Mock<IProductHistoryRepository> _productHistoryRepositoryStub;
	static ProductInformation _productInformation;
	static Mock<IProductRepository> _productRepositoryStub;
	static ProductService _productService;

	Establish context = () =>
		{
			_auditServiceStub = new Mock<IAuditService>();
			_productHistoryRepositoryStub = new Mock<IProductHistoryRepository>();
			_orderHistoryRepositoryStub = new Mock<IOrderHistoryRepository>();
			_orderReturnServiceStub = new Mock<IOrderReturnService>();
			_productRepositoryStub = new Mock<IProductRepository>();

			_productRepositoryStub.Setup(x => x.Get(ProductId)).Returns(new Product(ProductId, "Product description", 20.00m));
			_productRepositoryStub.Setup(x => x.GetRelatedProducts(ProductId))
				.Returns(new List<Product>
					        {
					         	new Product(RelatedProductId,
					         		          "Related product description",
					         		          30.10m)
					        });

			_productService = new ProductService(_auditServiceStub.Object, _productHistoryRepositoryStub.Object,
				                                    _orderHistoryRepositoryStub.Object, _orderReturnServiceStub.Object,
				                                    _productRepositoryStub.Object);
		};

	Because of = () => _productInformation = _productService.GetProductInformation(ProductId);

	It should_return_related_products = () => _productInformation.RelatedProducts.ShouldNotBeEmpty();
}
listing 2

The ProductService class in listing 2 fulfills a number of responsibilities including those dealing with related order history, product returns and various auditing needs. Though the specification is only concerned with verifying that related product information is retrieved, a number of unused test doubles are required to instantiate a ProductService instance which may lead to a false assumption about the role these dependencies play in behavior being validated.

Missing Context

In some cases, setup code may be factored out of a concrete test implementation to enable reuse by other tests, or perhaps merely to reduce the visible amount of setup code being used. This practice obscures the context of the test when all of the information necessary for understanding how the system is used to facilitate the expected behavior isn’t discernible.

Consider the following variation on the expired credit card specification:

public class when_processing_a_payment_with_an_expired_credit_card : PaymentContext
{
	static Exception _exception;

	Because of = () => _exception = Catch.Exception(() => PaymentGateway.Process(PaymentInformation));

	It should_throw_an_expired_card_exception = () => _exception.ShouldBeOfType<ExpiredCardException>();
}
listing 3

While this implementation doesn’t overwhelm its reader with incidental or non-cohesive setup needs, key pieces of information for understanding how the system is used to achieve the expected behavior are missing, namely, the type information for the key objects used and the input values necessary to trigger the expected behavior.

Guidelines for Avoiding Obscurity

To avoid writing obscure tests, information pertinent to understanding how the system is used to achieve the expected behavior should be discernible within the test’s concrete implementation. While what’s considered pertinent is somewhat subjective, the following are some guidelines for helping to avoid obscure tests:

  • Ensure the type of the System Under Test is discernible.
  • Ensure the input and return parameter types used by the methods being exercised are discernible.
  • Ensure all collaborating test double types and setup which are consequential to the behavior being validated is discernible.
  • Ensure all direct and indirect input values which are consequential to the behavior being validated are discernible.
  • Minimize any setup which is incidental to understanding the behavior being validated.
  • Refactor system components whose behavior results in setup needs unrelated to the area of concern being tested.

Strategies for Avoiding Obscurity

Base Fixtures

As previously noted, context obscurity can result from including both too much or too little context information. One strategy which can both cause or help to alleviate context obscurity is the use of Base Fixtures. Base fixtures are types which define setup and/or behavior which may be inherited by one or more concrete test cases. Base fixtures are commonly used to eliminate duplication across multiple test cases sharing the same context, but this often leads to obscurity due to the absence of setup information essential to understanding the derived test cases. Unlike the role stereotypes of other objects within a system, the role of executable specifications is to model the requirements of the system. The use of design principles and programming language capabilities should therefore be subjugated to this purpose.

One use of base fixtures which can reduce incidental context while preserving relevant context is the use of generic base fixtures. Establishing the System Under Test often requires the setup of test doubles to be used in the object’s instantiation. While any test double configuration needed for understanding a particular behavior of the system should be declared by the verifying test case, the instantiation of the System Under Test and any parameters needed are generally not pertinent to the test case. By allowing derived test cases to specify the type of the System Under Test as a generic parameter to the base fixture and providing methods for accessing any test doubles for unique setup needs, the non-essential portions of the context setup can be removed from the deriving test cases. This technique was demonstrated earlier in this series in the article Effective Tests: Auto-mocking Containers.

As a review, the following listing shows a base fixture which defines common code for setting up an auto-mocking container along with methods for configuring any test doubles used:

public abstract class WithSubject<T> where T : class
{
    protected static AutoMockContainer Container;
    protected static T Subject;

     Establish context = () =>
        {
            Container = new AutoMockContainer(new MockFactory(MockBehavior.Loose));
            Subject = Container.Create<T>();
        };

    protected static Mock<TDouble> For<TDouble>() where TDouble : class
    {
        return Container.GetMock<TDouble>();
    }
}
listing 4

Given this base fixture, the following specification can be derived which specifies the type of the System Under Test without including the ancillary concerns of setting up the Auto-mocking container or any Test Dummies required:

public class when_displaying_part_details : WithSubject<DisplayPartDetailsAction>
{
    const string PartId = "12345";

    Because of = () => Subject.Display(PartId);

    It should_retrieve_the_part_information_from_the_cache =
        () => For<ICachingService>().Verify(x => x.RetrievePartDetails(PartId), Times.Exactly(1));
}
listing 5

Base fixtures can be a benefit or a detriment to the clarity of our tests. When used, care should be taken to ensure the context of each deriving test case can be understood without needing to consult its base fixture.

Object Mothers

One of the specific types of setup needs which can lead to obscurity is the setup of test data. It is often necessary to construct test data objects to be used as either parameters, test stub values, or expected objects which can begin to dilute the clarity of a test as such needs increase. In some cases, the actual test data values aren’t pertinent to the subject of the test (e.g. an application submitted after the deadline), or a meaningful configuration of test data values can be abstracted into logical, well-known entities (e.g. a valid application). In such cases, the setup of test data can be delegated to an Object Mother. An Object Mother is a specialized factory whose role is to create canned test data objects.

The following demonstrates an Object Mother which provides canned Order objects:

public static class OrderObjectMother
{
	public static Order CreateOrder()
	{
		var cart = new Cart();
		cart.AddItem(new Guid(), 2);
		var billingInformation = new BillingInformation
			                        {
			                         	BillingAddress = new Address
			                         		                {
			                         		                 	Name = "John Doe",
			                         		                 	AddressLineOne = "123 Street",
			                         		                 	City = "Springfield",
			                         		                 	StateCode = "MO",
			                         		                 	Zipcode = "65807"
			                         		                },
			                         	CreditCardNumber = "41111111111111",
			                         	CreditCardType = CardType.Visa,
			                         	ExpirationDate = Date.Today.Subtract(TimeSpan.FromDays(1)),
			                         	CardHolderName = "John P. Doe",
			                        };

		var shippingInformation = new ShippingInformation(billingInformation);

		return new Order(cart, billingInformation, shippingInformation);
	}
}
listing 6

Note that the CreateOrder() method is required to create several intermediate objects to build up an instance of the Order object. If these intermediate objects are required by other specifications, these might be factored out into their own Object Mother factories.

By delegating the creation of an Order object to the Object Mother in listing 6, the following specification can be implemented with minimal visible context setup while preserving the essence of the declared context information:

public class when_placing_a_valid_order : WithSubject<OrderService>
{
	static Order _order;
	static OrderReceipt _receipt;

	Establish context = () => _order = OrderObjectMother.CreateOrder();

	Because of = () => _receipt = Subject.PlaceOrder(_order);

	It should_return_the_order_number = () => _receipt.OrderNumber.ShouldNotBeNull();
}
listing 7

Since the specification in listing 7 concerns what happens when a valid order is placed rather than what constitutes a valid order, there is no need to show the values contained by the Order object created.

If a variation of the object is needed, new methods can be added to the Object Mother to denote the variation. The following specification assumes the existence of an additional factory method used to validate behavior associated with invalid orders:

public class when_placing_an_invalid_order : WithSubject<OrderService>
{
	static Exception _exception;
	static Order _invalidOrder;

	Establish context = () => _invalidOrder = OrderObjectMother.CreateInvalidOrder();

	Because of = () => _exception = Catch.Exception(() => Subject.PlaceOrder(_invalidOrder));

	It should_throw_an_invalid_order_exception = () => _exception.ShouldBeOfType<InvalidOrderException>();
}
listing 8

Test Builders

While Object Mothers provide a nice way to retrieve canned test data, they don’t present an elegant way to deal with variability. For cases where a number of variations of the test data are needed, or when a subset of the values required for setting up test data objects are relevant to the declaring test case, the test data objects can be created using Test Builders. Test Builders are based upon the Builder pattern which creates objects based upon the accumulation of information from successive method calls terminated by a final construction method.

The following demonstrates a Test Builder for creating variations of an Order object:

public class OrderBuilder
{
	readonly BillingInformation _billingInformation;
	readonly ShippingInformation _shippingInformation;
	Cart _cart;

	public OrderBuilder()
	{
		_cart = new Cart();
		_cart.AddItem(new Guid(), 2);
			
		_billingInformation = new BillingInformation
		{
			BillingAddress = new Address
			{
				Name = "John Doe",
				AddressLineOne = "123 Street",
				City = "Springfield",
				StateCode = "MO",
				Zipcode = "65807"
			},
			CreditCardNumber = "41111111111111",
			CreditCardType = CardType.Visa,
			ExpirationDate = Date.Today.Subtract(TimeSpan.FromDays(1)),
			CardHolderName = "John P. Doe",
		};

		_shippingInformation = new ShippingInformation(_billingInformation);
	}

	public OrderBuilder WithCreditCardNumber(string creditCardNumber)
	{
		_billingInformation.CreditCardNumber = creditCardNumber;
		return this;
	}

	public OrderBuilder WithExpirationDate(Date expirationDate)
	{
		_billingInformation.ExpirationDate = expirationDate;
		return this;
	}

	public OrderBuilder WithCreditCardType(CardType cardType)
	{
		_billingInformation.CreditCardType = cardType;
		return this;
	}

	public OrderBuilder WithCardHolderName(string cardHolderName)
	{
		_billingInformation.CardHolderName = cardHolderName;
		return this;
	}

	public OrderBuilder WithCart(Cart cart)
	{
		_cart = cart;
		return this;
	}

	public Order Build()
	{
		return new Order(_cart, _billingInformation, _shippingInformation);
	}
}
listing 9

The following specification demonstrates how the Test Builder in listing 9 might be used to validate the results of placing an order with an invalid credit card:

public class when_placing_an_order_with_an_invalid_credit_card : WithSubject<OrderService>
{
	static Exception _exception;
	static Order _invalidOrder;

	Establish context = () => _invalidOrder = new OrderBuilder()
		                                          .WithCreditCardNumber("12345")
		                                          .Build();

	Because of = () => _exception = Catch.Exception(() => Subject.PlaceOrder(_invalidOrder));


	It should_throw_an_invalid_credit_card_exception = () => _exception.ShouldBeOfType<InvalidCreditCardException>();
}
listing 10

Conclusion

In this article, we considered several causes of context obscurity and discussed a few ways of avoiding it. Next time, we’ll move on to the topic of writing automated acceptance tests.

Tagged with:  

Introducing the Expected Objects Library

On June 28, 2011, in Uncategorized, by derekgreer

Introduced in the Effective Test Series, the Expected Object pattern is a technique involving the encapsulation of test-specific logic within a specialized type designed to compare its configured state against that of another object. Use of the Expected Object pattern eliminates the need to encumber system objects with test-specific equality behavior, helps to reduce test code duplication and can aid in expressing the logical intent of automated tests.

While the Expected Object pattern is a great strategy for helping adhere to good testing practices, the process of actually implementing the required types can be less than motivating. To alleviate the burden of hand-rolling Expected Object types, I created the Expected Objects library. This library provides the ability to compare the state of one object against another without relying upon the provided type’s equality members. In addition to the ability to assert equality, the library also provides equality assertion methods which provide feedback of how each member of an object differs from an expected state.

The following examples demonstrate the capabilities of the library:

 

Comparing Flat Objects

public class when_retrieving_a_customer
{
	static Customer _actual;
	static ExpectedObject _expected;

	Establish context = () =>
		{
			_expected = new Customer
				            {
				            	Name = "Jane Doe",
				            	PhoneNumber = "5128651000"
				            }.ToExpectedObject();

			_actual = new Customer
				          {
				          	Name = "John Doe",
				          	PhoneNumber = "5128654242"
				          };
		};

	It should_return_the_expected_customer = () => _expected.ShouldEqual(_actual);
}



class Customer
{
	public string Name { get; set; }
	public string PhoneNumber { get; set; }
}

Results:

should return the expected customer : Failed For Customer.Name, expected "Jane Doe" but found "John Doe". For Customer.PhoneNumber, expected "5128651000" but found "5128654242".

 

Comparing Composed Objects

public class when_retrieving_a_customer_with_address
{
	static Customer _actual;
	static ExpectedObject _expected;

	Establish context = () =>
		{
			_expected = new Customer
				            {
				            	Name = "Jane Doe",
				            	PhoneNumber = "5128651000",
				            	Address = new Address
				            		          {
				            		          	AddressLineOne = "123 Street",
				            		          	AddressLineTwo = string.Empty,
				            		          	City = "Austin",
				            		          	State = "TX",
				            		          	Zipcode = "78717"
				            		          }
				            }.ToExpectedObject();

			_actual = new Customer
				          {
				          	Name = "John Doe",
				          	PhoneNumber = "5128654242",
				          	Address = new Address
				          		          {
				          		          	AddressLineOne = "456 Street",
				          		          	AddressLineTwo = "Apt. 3",
				          		          	City = "Waco",
				          		          	State = "TX",
				          		          	Zipcode = "76701"
				          		          }
				          };
		};

	It should_return_the_expected_customer = () => _expected.ShouldEqual(_actual);
}



class Customer
{
	public string Name { get; set; }
	public string PhoneNumber { get; set; }
	public Address Address { get; set; }
}



class Address
{
	public string AddressLineOne { get; set; }
	public string AddressLineTwo { get; set; }
	public string City { get; set; }
	public string State { get; set; }
	public string Zipcode { get; set; }
}

Results:

should return the expected customer : Failed For Customer.Name, expected "Jane Doe" but found "John Doe". For Customer.PhoneNumber, expected "5128651000" but found "5128654242". For Customer.Address.AddressLineOne, expected "123 Street" but found "456 Street". For Customer.Address.AddressLineTwo, expected "" but found "Apt. 3". For Customer.Address.City, expected "Austin" but found "Waco". For Customer.Address.Zipcode, expected "78717" but found "76701".

 

Comparing Collections

public class when_retrieving_a_collection_of_customers
{
	static List<Customer> _actual;
	static ExpectedObject _expected;

	Establish context = () =>
		{
			_expected = new List<Customer>
				            {
				            	new Customer {Name = "Customer A"},
				            	new Customer {Name = "Customer B"}
				            }.ToExpectedObject();

			_actual = new List<Customer>
				          {
				          	new Customer {Name = "Customer A"},
				          	new Customer {Name = "Customer C"}
				          };
		};

	It should_return_the_expected_customers = () => _expected.ShouldEqual(_actual);
}

Results:

should return the expected customers : Failed For List`1[1].Name, expected "Customer B" but found "Customer C".

 

Comparing Dictionaries

public class when_retrieving_a_dictionary
{
	static IDictionary<string, string> _actual;
	static IDictionary<string, string> _expected;

	static bool _result;

	Establish context = () =>
		{
			_expected = new Dictionary<string, string> {{"key1", "value1"}};
			_actual = new Dictionary<string, string> {{"key1", "value1"}, {"key2", "value2"}};
		};

	It should_return_the_expected_dictionary = () => _expected.ToExpectedObject().ShouldEqual(_actual);
}

Results:

should return the expected dictionary : Failed For Dictionary`2[1], expected nothing but found [[key2, value2]].

 

Comparing Types with Indexes

public class when_retrieving_a_type_with_an_index
{
	static IndexType _actual;
	static IndexType _expected;

	static bool _result;

	Establish context = () =>
		{
			_expected = new IndexType(new List<int> {1, 2, 3, 4, 6});
			_actual = new IndexType(new List<int> {1, 2, 3, 4, 5});
		};

	It should_return_the_expected_type = () => _expected.ToExpectedObject().ShouldEqual(_actual);
}



class IndexType
{
	readonly IList<T> _ints;

	public IndexType(IList<T> ints)
	{
		_ints = ints;
	}

	public T this[int index]
	{
		get { return _ints[index]; }
	}

	public int Count
	{
		get { return _ints.Count; }
	}
}

Results:

should return the expected type : Failed For IndexType`1.Item[4], expected [6] but found [5].

 

Comparing Partial Objects

public class when_retrieving_a_customer
{
	static Customer _actual;
	static ExpectedObject _expected;

	Establish context = () =>
		{
			_expected = new
				            {
				            	Name = "Jane Doe",
				            	Address = new
				            		          {
				            		          	City = "Austin"
				            		          }
				            }.ToExpectedObject();

			_actual = new Customer
				          {
				          	Name = "John Doe",
				          	PhoneNumber = "5128654242",
				          	Address = new Address
				          		          {
				          		          	AddressLineOne = "456 Street",
				          		          	AddressLineTwo = "Apt. 3",
				          		          	City = "Waco",
				          		          	State = "TX",
				          		          	Zipcode = "76701"
				          		          }
				          };
		};

	It should_have_the_correct_name_and_address = () => _expected.ShouldMatch(_actual);
}
Results:
should have the correct name and address : Failed For Customer.Name, expected "Jane Doe" but found "John Doe". For Customer.Address.City, expected "Austin" but found "Waco".

 

Extensibility

The Expected Objects library is extensible, so if it doesn’t provide the exact comparison strategies you need then you’re free to add our own.

The main extensibility point is the IComparisonStrategy which is declared as follows:

public interface IComparisonStrategy
{
    bool CanCompare(Type type);
    bool AreEqual(object expected, object actual, IComparisonContext comparisonContext);
}

To register a custom strategy, simply call the Configure() method and use the supplied ConfigurationContext to call the PushStrategy<t>() method:

_expected = new Foo("Bar")
	.ToExpectedObject()
	.Configure(ctx => ctx.PushStrategy<FooComparisonStrategy>());

This will push the custom strategy onto the stack used by the Expected Objects library during its comparisons.

Custom Comparison Strategy Example

The following demonstrates how the Expected Objects library could be extended to compare an expected object to the contents of a Web page.

Consider the following specification:

public class when_displaying_the_customer_view
{
	static Mock<IWebDriver> _actual;
	static ExpectedObject _expected;

	Establish context = () =>
		{
			var nameElementStub = new Mock<IWebElement>();
			nameElementStub.Setup(x => x.Text).Returns("Jane Doe");
			var addressElementStub = new Mock<IWebElement>();
			addressElementStub.Setup(x => x.Text).Returns("456 Street");
			var buttonElementStub = new Mock<IWebElement>();
			buttonElementStub.Setup(x => x.Text).Returns("Cancel");
			_actual = new Mock<IWebDriver>();
			_actual.Setup(x => x.FindElement(By.Id("name"))).Returns(nameElementStub.Object);
			_actual.Setup(x => x.FindElement(By.CssSelector("input[name='address']"))).Returns(addressElementStub.Object);
			_actual.Setup(x => x.FindElement(By.XPath("//input[@value='submit']"))).Returns(buttonElementStub.Object);

			_expected = new ExpectedView()
				.WithId("name", "John Doe")
				.WithCssSelector("input[name='address']", "123 Street")
				.WithXPath("//input[@value='submit']", "Submit")
				.ToExpectedObject()
				.Configure(ctx =>
					{
						ctx.PushStrategy<ExpecedViewComparisonStrategy>();
						ctx.IgnoreTypes();
					});
		};

	It should_display_the_expected_view = () => _expected.ShouldEqual(_actual.Object);
}

Here, the Selenium 2 IWebDriver type is being stubbed to emulate an active Selenium testing session. Next, a custom ExpectedView type is instantiated and configured to expect one value to be located by an Id, one by a CSS Selector and one by an XPath. Lastly, the expected object is compared to the actual object (in this case, the IWebDriver stub).

Executing the specification produces the following results:

should display the expected view : Failed For IWebDriverProxy.FindElement(By.Id("name")), expected "John Doe" but found "Jane Doe". For IWebDriverProxy.FindElement(By.CssSelector("input[name='address']")), expected "123 Street" but found "456 Street". For IWebDriverProxy.FindElement(By.XPath("//input[@value='submit']")), expected "Submit" but found "Cancel".

 

Here is the ExpectedView and ExpectedViewComparisonStrategy implementation:

class ExpectedView
{
	public ExpectedView()
	{
		Ids = new List<Tuple<string, string>>();
		CssSelectors = new List<Tuple<string, string>>();
		XPaths = new List<Tuple<string, string>>();
	}

	public List<Tuple<string, string>> Ids { get; private set; }
	public List<Tuple<string, string>> CssSelectors { get; private set; }
	public List<Tuple<string, string>> XPaths { get; private set; }

	public ExpectedView WithId(string name, string value)
	{
		Ids.Add(new Tuple<string, string>(name, value));
		return this;
	}

	public ExpectedView WithCssSelector(string selector, string value)
	{
		CssSelectors.Add(new Tuple<string, string>(selector, value));
		return this;
	}

	public ExpectedView WithXPath(string path, string value)
	{
		XPaths.Add(new Tuple<string, string>(path, value));
		return this;
	}
}



class ExpecedViewComparisonStrategy : IComparisonStrategy
{
	public bool CanCompare(Type type)
	{
		return typeof (ExpectedView).IsAssignableFrom(type);
	}

	public bool AreEqual(object expected, object actual, IComparisonContext comparisonContext)
	{
		bool areEqual = true;
		var view = (ExpectedView) expected;
		var driver = (IWebDriver) actual;
		view.Ids.ForEach(id => areEqual = CompareIds(driver, id, comparisonContext) && areEqual);
		view.CssSelectors.ForEach(selector => areEqual = CompareCssSelectors(driver, selector, comparisonContext) && areEqual);
		view.XPaths.ForEach(path => areEqual = CompareXPaths(driver, path, comparisonContext) && areEqual);
		return areEqual;
	}

	static bool CompareIds(IWebDriver driver, Tuple<string, string> expected, IComparisonContext comparisonContext)
	{
		bool areEqual = true;
		IWebElement idElement = driver.FindElement(By.Id(expected.Item1));
		areEqual = comparisonContext.AreEqual(expected.Item2, idElement.Text, "FindElement(By.Id(\"" + expected.Item1 + "\"))") && areEqual;
		return areEqual;
	}

	static bool CompareCssSelectors(IWebDriver driver, Tuple<string, string> expected, IComparisonContext comparisonContext)
	{
		bool areEqual = true;
		IWebElement idElement = driver.FindElement(By.CssSelector(expected.Item1));
		areEqual = comparisonContext.AreEqual(expected.Item2, idElement.Text, "FindElement(By.CssSelector(\"" + expected.Item1 + "\"))") && areEqual;
		return areEqual;
	}

	static bool CompareXPaths(IWebDriver driver, Tuple<string, string> expected, IComparisonContext comparisonContext)
	{
		bool areEqual = true;
		IWebElement idElement = driver.FindElement(By.XPath(expected.Item1));
		areEqual = comparisonContext.AreEqual(expected.Item2, idElement.Text, "FindElement(By.XPath(\"" + expected.Item1 + "\"))") && areEqual;
		return areEqual;
	}
}

Note: This example is for demonstration purposes only.

 

Conclusion

The Expected Objects library is published as a NuGet package and the source is hosted on github. Feel free to provide feedback.

Tagged with:  

Effective Tests: Expected Objects

On June 24, 2011, in Uncategorized, by derekgreer
This entry is part 15 of 17 in the series Effective Tests

In the last installment of the Effective Tests series, the topic of Custom Assertions was presented as a strategy for helping to clarify the intent of our tests. This time we’ll take a look at another test pattern for improving the communication of our tests in addition to reducing test code duplication and the need to add test-specific code to our production types.

Expected Objects

Writing tests often involves inspecting the state of collaborating objects and the messages they exchange within a system. This often leads to declaring multiple assertions on fields of the same object which can lead to several maintenance issues. First, if multiple specifications need to verify the same values then this can result in test code duplication. For example, two searches for a customer record with different criteria may be expected to return the same result. Second, when many fine-grained assertions are performed within a specification, the overall purpose can become obscured. For example, a specification may indicate that a value returned from an order process “should contain a first name and a last name and a home phone number and an address line 1 and …” while the intended perspective may be that the operation “should return a shipment confirmation”.

One solution to this problem is to override an object’s equality operators and/or methods to suit the needs of the test. Unfortunately, this is not without its own set of issues. Aside from introducing behavior into the system which is only exercised by the tests, this strategy may conflict with the existing or future needs of the system due to a difference in how each define equality for the objects being compared. While a test may need to compare all the properties of two objects, the system may require equality to be based upon the object’s identity (e.g. two customers are the same if they have the same customer Id). It may happen that the system already defines equality suitable to the needs of the test, but this is subject to change. A system may compare two objects by value for the purposes of indexing, ensuring cardinality, or an assortment of domain-specific reasons whose needs may change as the system evolves. While the initial state of an object’s definition of equality may coincide with the needs of the test, the needs of both represent two axes of change which could lead to higher maintenance costs if not dealt with separately.

When using state-based verification, one way of avoiding test code duplication, obscurity and the need to equip the system with test-specific equality code is to implement the Expected Object pattern. The Expected Object pattern defines objects which encapsulate test-specific equality separate from the objects they are compared against. An expected object may be implemented as a sub-type whose equality members have been overloaded to perform the desired comparisons or as a test-specific type designed to compare itself against another object type.

Consider the following specification which validates that placing an order returns an order receipt populated with the expected values:

[Subject(typeof (OrderService))]
public class when_an_order_is_placed : WithSubject<OrderService>
{
	static readonly Guid CustomerId = new Guid("061F3CED-405F-4261-AF8C-AA2B0694DAD8");
	const long OrderNumber = 1L;
	static Customer _customer;
	static Order _order;
	static OrderReceipt _orderReceipt;


	Establish context = () =>
		{
			_customer = new TestCustomer(CustomerId)
				            {
				            	FirstName = "First",
				            	LastName = "Last",
				            	PhoneNumber = "5129130000",
				            	Address = new Address
				            		        {
				            		          	LineOne = "123 Street",
				            		          	LineTwo = string.Empty,
				            		          	City = "Austin",
				            		          	State = "TX",
				            		          	ZipCode = "78717"
				            		        }
				            };
			For<IOrderNumberProvider<long>>().Setup(x => x.GetNext()).Returns(OrderNumber);
			For<ICustomerRepository>().Setup(x => x.Get(Parameter.IsAny<Guid>())).Returns(_customer);
			_order = new Order(1, "Product A");
		};

	Because of = () => _orderReceipt = Subject.PlaceOrder(_order, _customer.Id);

	It should_return_a_receipt_with_order_number = () => _orderReceipt.OrderNumber.ShouldEqual(OrderNumber.ToString());

	It should_return_a_receipt_with_order_description = () => _orderReceipt.Orders.ShouldContain(_order);

	It should_return_a_receipt_with_customer_id = () => _orderReceipt.CustomerId.ShouldEqual(_customer.Id.ToString());
		
	It should_return_an_order_receipt_with_customer_name = () => _orderReceipt.CustomerName.ShouldEqual(_customer.FirstName + " " + _customer.LastName);

	It should_return_a_receipt_with_customer_phone = () => _orderReceipt.CustomerPhone.ShouldEqual(_customer.PhoneNumber);

	It should_return_a_receipt_with_address_line_1 = () => _orderReceipt.AddressLineOne.ShouldEqual(_customer.Address.LineOne);

	It should_return_a_receipt_with_address_line_2 = () => _orderReceipt.AddressLineTwo.ShouldEqual(_customer.Address.LineTwo);
		
	It should_return_a_receipt_with_city = () => _orderReceipt.City.ShouldEqual(_customer.Address.City);

	It should_return_a_receipt_with_state = () => _orderReceipt.State.ShouldEqual(_customer.Address.State);

	It should_return_a_receipt_with_zip = () => _orderReceipt.ZipCode.ShouldEqual(_customer.Address.ZipCode);
}
Listing 1

While the specification in listing 1 provides ample detail about the values that should be present on the returned receipt, such an implementation precludes reuse and tends to overwhelm the purpose of the specification. This problem is further compounded as the composition complexity increases.

As an alternative to declaring what each field of a particular object should contain, the Expected Object pattern allows you to declare what a particular object should look like. By replacing the specification’s discrete assertions with a single assertion comparing an Expected Object against a resulting state, the essence of the specification can be preserved while maintaining an equivalent level of verification.

Consider the following simple implementation for an Expected Object:

class ExpectedOrderReceipt : OrderReceipt
{
	public override bool Equals(object obj)
	{
		var otherReceipt = obj as OrderReceipt;

		return OrderNumber.Equals(otherReceipt.OrderNumber) &&
			    CustomerId.Equals(otherReceipt.CustomerId) &&
			    CustomerName.Equals(otherReceipt.CustomerName) &&
			    CustomerPhone.Equals(otherReceipt.CustomerPhone) &&
			    AddressLineOne.Equals(otherReceipt.AddressLineOne) &&
			    AddressLineTwo.Equals(otherReceipt.AddressLineTwo) &&
			    City.Equals(otherReceipt.City) &&
			    State.Equals(otherReceipt.State) &&
			    ZipCode.Equals(otherReceipt.ZipCode) &&
			    Orders.ToList().SequenceEqual(otherReceipt.Orders);
	}
}
Listing 2

Establishing an instance of the expected object in listing 2 allows the previous discrete assertions to be replaced with a single assertion declaring what the returned receipt should look like:

[Subject(typeof (OrderService))]
public class when_an_order_is_placed : WithSubject<OrderService>
{
	const long OrderNumber = 1L;
	static readonly Guid CustomerId = new Guid("061F3CED-405F-4261-AF8C-AA2B0694DAD8");
	static Customer _customer;
	static ExpectedOrderReceipt _expectedOrderReceipt;
	static Order _order;
	static OrderReceipt _orderReceipt;


	Establish context = () =>
		{
			_customer = new TestCustomer(CustomerId)
				            {
				            	FirstName = "First",
				            	LastName = "Last",
				            	PhoneNumber = "5129130000",
				            	Address = new Address
				            		        {
				            		          	LineOne = "123 Street",
				            		          	LineTwo = string.Empty,
				            		          	City = "Austin",
				            		          	State = "TX",
				            		          	ZipCode = "78717"
				            		        }
				            };
			For<IOrderNumberProvider<long>>().Setup(x => x.GetNext()).Returns(OrderNumber);
			For<ICustomerRepository>().Setup(x => x.Get(Parameter.IsAny<Guid>())).Returns(_customer);
			_order = new Order(1, "Product A");

			_expectedOrderReceipt = new ExpectedOrderReceipt
				                        {
				                        	OrderNumber = OrderNumber.ToString(),
				                        	CustomerName = "First Last",
				                        	CustomerPhone = "5129130000",
				                        	AddressLineOne = "123 Street",
				                        	AddressLineTwo = string.Empty,
				                        	City = "Austin",
				                        	State = "TX",
				                        	ZipCode = "78717",
				                        	CustomerId = CustomerId.ToString(),
				                        	Orders = new List<Order> {_order}
				                        };
		};

	Because of = () => _orderReceipt = Subject.PlaceOrder(_order, _customer.Id);

	It should_return_an_receipt_with_shipping_information_and_order_number =
		() => _expectedOrderReceipt.Equals(_orderReceipt).ShouldBeTrue();
}
Listing 3

The implementation strategy in listing 3 offers a subtle shift in perspective, but one which may more closely model the language of the business.

This is not to say that discrete assertions are always wrong. The level of detail modeled by an application’s specifications should be based upon the needs of the business. Consider the test runner output for both implementations:

ExpectedObjectContrast

Figure 1

Examining the results of executing both specifications in figure 1, we see that the first describes each field being validated, while the second describes what the validations of these fields collectively mean. Which is best will depend upon your particular business needs. While the first implementation provides a more detailed specification of the receipt, this may or may not be as important to the business as knowing that the receipt as a whole is correct. For example, consider if the order number were missing. Is the correct perspective that the receipt is 90% correct or 100% wrong? The correct answer is … it depends.

Explicit Feedback

While the Expected Object implementation shown in listing 2 may be an adequate approach in some cases, it does have the shortcoming of not providing explicit feedback of how the two objects differ. To address this, we can implement our Expected Object as a Custom Assertion. Instead of asserting on the return value of comparing the expected object to an object returned from our system, we can design the Expected Object to throw an exception detailing what state differed between the two objects. The following listing demonstrates this approach:

class ExpectedOrderReceipt : OrderReceipt
{
	public void ShouldEqual(object obj)
	{
		var otherReceipt = obj as OrderReceipt;
		var messages = new List<string>();

		if (!OrderNumber.Equals(otherReceipt.OrderNumber))
			messages.Add(string.Format("For OrderNumber, expected '{0}' but found '{1}'", OrderNumber, otherReceipt.OrderNumber));

		if (!CustomerId.Equals(otherReceipt.CustomerId))
			messages.Add(string.Format("For CustomerId, expected '{0}' but found '{1}'", CustomerId, otherReceipt.CustomerId));

		if (!CustomerName.Equals(otherReceipt.CustomerName))
			messages.Add(string.Format("For CustomerName, expected '{0}' but found '{1}'", CustomerName, otherReceipt.CustomerName));

		if (!CustomerPhone.Equals(otherReceipt.CustomerPhone))
			messages.Add(string.Format("For CustomerPhone, expected '{0}' but found '{1}'", CustomerPhone, otherReceipt.CustomerPhone));

		if (!AddressLineOne.Equals(otherReceipt.AddressLineOne))
			messages.Add(string.Format("For AddressLineOne, expected '{0}' but found '{1}'", AddressLineOne, otherReceipt.AddressLineOne));

		if (!AddressLineTwo.Equals(otherReceipt.AddressLineTwo))
			messages.Add(string.Format("For AddressLineTwo, expected '{0}' but found '{1}'", AddressLineTwo, otherReceipt.AddressLineOne));

		if (!City.Equals(otherReceipt.City))
			messages.Add(string.Format("For City, expected '{0}' but found '{1}'", City, otherReceipt.City));

		if (!State.Equals(otherReceipt.State))
			messages.Add(string.Format("For State, expected '{0}' but found '{1}'", State, otherReceipt.State));

		if (!ZipCode.Equals(otherReceipt.ZipCode))
			messages.Add(string.Format("For ZipCode, expected '{0}' but found '{1}'", ZipCode, otherReceipt.ZipCode));

		if (!Orders.ToList().SequenceEqual(otherReceipt.Orders))
			messages.Add("For Orders, expected the same sequence but was different.");

		if(messages.Count > 0)
			throw new Exception(string.Join(Environment.NewLine, messages));
	}
}
Listing 4

The following listing shows the specification modified to use the new Expected Object implementation with several values on the TestCustomer modified to return values differing from the expected value:

[Subject(typeof (OrderService))]
public class when_an_order_is_placed : WithSubject<OrderService>
{
	const long OrderNumber = 1L;
	static readonly Guid CustomerId = new Guid("061F3CED-405F-4261-AF8C-AA2B0694DAD8");
	static Customer _customer;
	static ExpectedOrderReceipt _expectedOrderReceipt;
	static Order _order;
	static OrderReceipt _orderReceipt;


	Establish context = () =>
		{
			_customer = new TestCustomer(CustomerId)
				            {
				            	FirstName = "Wrong",
				            	LastName = "Wrong",
				            	PhoneNumber = "Wrong",
				            	Address = new Address
				            		        {
				            		          	LineOne = "Wrong",
				            		          	LineTwo = "Wrong",
				            		          	City = "Austin",
				            		          	State = "TX",
				            		          	ZipCode = "78717"
				            		        }
				            };
			For<IOrderNumberProvider<long>>().Setup(x => x.GetNext()).Returns(OrderNumber);
			For<ICustomerRepository>().Setup(x => x.Get(Parameter.IsAny<Guid>())).Returns(_customer);
			_order = new Order(1, "Product A");

			_expectedOrderReceipt = new ExpectedOrderReceipt
				                        {
				                        	OrderNumber = OrderNumber.ToString(),
				                        	CustomerName = "First Last",
				                        	CustomerPhone = "5129130000",
				                        	AddressLineOne = "123 Street",
				                        	AddressLineTwo = string.Empty,
				                        	City = "Austin",
				                        	State = "TX",
				                        	ZipCode = "78717",
				                        	CustomerId = CustomerId.ToString(),
				                        	Orders = new List<Order> {_order}
				                        };
		};

	Because of = () => _orderReceipt = Subject.PlaceOrder(_order, _customer.Id);

	It should_return_an_receipt_with_shipping_and_order_information = () => _expectedOrderReceipt.ShouldEqual(_orderReceipt);
}
Listing 5

Running the specification produces the following output:

ExpectedObjectExplicitFeedback
Figure 2

Conclusion

This time, we took a look at the Expected Object pattern which aids in reducing code duplication, eliminating the need to put test-specific equality behavior in our production code and serves as a strategy for further clarifying the intent of our specifications. Next time, we’ll look at some strategies for combating obscurity and test-code duplication caused by test data.

Tagged with:  

Effective Tests: Custom Assertions

On June 11, 2011, in Uncategorized, by derekgreer
This entry is part 14 of 17 in the series Effective Tests

In our last installment, we took a look at using Auto-mocking Containers as a way of reducing coupling and obscurity within our tests. This time, we’ll take a look at another technique which aids in preventing obscurity caused by complex assertions: Custom Assertions.

Custom Assertions

As discussed earlier in the series, executable specifications are a model for our requirements. Whether serving as a guide for our implementation or as documentation for existing behavior, specifications should be easy to understand. Assertion implementation is one of the areas that can often begin to obscure the intent of our specifications. When standard assertions fall short of expressing what is being validated clearly and concisely, they can be replaced with Custom Assertions. Custom assertions are domain-specific assertions which encapsulate complex or obscure testing logic within intention-revealing methods.

Consider the following example which validates that the items returned from a ReviewService class are sorted in descending order:

    public class when_a_customer_retrieves_comment_history : WithSubject<ReviewService>
    {
        const string ItemId = "123";
        static IEnumerable<Comment> _comments;

        Establish context = () => For<IItemRepository>()
                                      .Setup(x => x.Get(ItemId))
                                      .Returns(new Item(new[]
                                                            {
                                                                new Comment("comment 1", DateTime.MinValue.AddDays(1)),
                                                                new Comment("comment 2", DateTime.MinValue.AddDays(2)),
                                                                new Comment("comment 3", DateTime.MinValue.AddDays(3))
                                                            }));

        Because of = () => _comments = Subject.GetCommentsForItem(ItemId);
      
        It should_return_comments_sorted_by_date_in_descending_order = () =>
            {
                Comment[] commentsArray = _comments.ToArray();
                for (int i = commentsArray.Length - 1; i > 0; i--)
                {
                    if (commentsArray[i].TimeStamp > commentsArray[i - 1].TimeStamp)
                    {
                        throw new Exception(
                            string.Format(
                                "Expected comments sorted in descending order, but found comment \'{0}\' on {1} after \'{2}\' on {3}",
                                commentsArray[i].Text, commentsArray[i].TimeStamp, commentsArray[i - 1].Text,
                                commentsArray[i - 1].TimeStamp));
                    }
                }
            };
    }

 

While the identifiers used to describe the specification are clear, the observation’s implementation contains a significant amount of verification logic which makes the specification more difficult to read. By moving the verification logic into a custom assertion which describes what is expected, we can clarify the specification’s intent.

When developing on the .Net platform, Extension Methods provide a nice way of encapsulating assertion logic while achieving an expressive API. The following listing shows the same assertion logic contained within an extension method:

    public static class CustomAssertions
    {
        public static void ShouldBeSortedByDateInDescendingOrder(this IEnumerable<Comment> comments)
        {
            Comment[] commentsArray = comments.ToArray();
            for (int i = commentsArray.Length - 1; i > 0; i--)
            {
                if (commentsArray[i].TimeStamp > commentsArray[i - 1].TimeStamp)
                {
                    throw new Exception(
                        string.Format(
                            "Expected comments sorted in descending order, but found comment \'{0}\' on {1} after \'{2}\' on {3}",
                            commentsArray[i].Text, commentsArray[i].TimeStamp, commentsArray[i - 1].Text,
                            commentsArray[i - 1].TimeStamp));
                }
            }
        }
    }

 

Using this new custom assertion, the specification can be rewritten to be more intention-revealing:

    public class when_a_customer_retrieves_comment_history : WithSubject<ReviewService>
    {
        const string ItemId = "123";
        static IEnumerable<Comment> _comments;

        Establish context = () => For<IItemRepository>()
                                      .Setup(x => x.Get(ItemId))
                                      .Returns(new Item(new[]
                                                            {
                                                                new Comment("comment 1", DateTime.MinValue.AddDays(1)),
                                                                new Comment("comment 2", DateTime.MinValue.AddDays(2)),
                                                                new Comment("comment 3", DateTime.MinValue.AddDays(3))
                                                            }));

        Because of = () => _comments = Subject.GetCommentsForItem(ItemId);

        It should_return_comments_sorted_by_date_in_descending_order = () => _comments.ShouldBeSortedByDateInDescendingOrder();
    }

Verifying Assertion Logic

Another advantage of factoring out complex assertion logic into custom assertion methods is the ability to verify that the logic actually works as expected. This can be particularly valuable if the assertion logic is reused by many specifications.

The following listing shows positive and negative tests for our custom assertion:

    public class when_asserting_unsorted_comments_are_sorted_in_descending_order
    {
        static Exception _exception;
        static List<Comment> _unsortedComments;

        Establish context = () =>
            {
                _unsortedComments = new List<Comment>
                                        {
                                            new Comment("comment 1", DateTime.MinValue.AddDays(1)),
                                            new Comment("comment 4", DateTime.MinValue.AddDays(4)),
                                            new Comment("comment 3", DateTime.MinValue.AddDays(3)),
                                            new Comment("comment 2", DateTime.MinValue.AddDays(2))
                                        };
            };

        Because of = () => _exception = Catch.Exception(() => _unsortedComments.ShouldBeSortedByDateInDescendingOrder());

        It should_throw_an_exception = () => _exception.ShouldBeOfType(typeof(Exception));
    }

    public class when_asserting_sorted_comments_are_sorted_in_descending_order
    {
        static Exception _exception;
        static List<Comment> _unsortedComments;

        Establish context = () =>
        {
            _unsortedComments = new List<Comment>
                                        {
                                            new Comment("comment 4", DateTime.MinValue.AddDays(4)),
                                            new Comment("comment 3", DateTime.MinValue.AddDays(3)),
                                            new Comment("comment 2", DateTime.MinValue.AddDays(2)),
                                            new Comment("comment 1", DateTime.MinValue.AddDays(1))
                                        };
        };

        Because of = () => _exception = Catch.Exception(() => _unsortedComments.ShouldBeSortedByDateInDescendingOrder());

        It should_not_throw_an_exception = () => _exception.ShouldBeNull();
    }

Conclusion

This time, we examined a simple strategy for clarifying the intent of our specifications involving the movement of complex verification logic into custom assertions methods. Next time we’ll take a look at another strategy for clarifying the intent of our specifications which also serves to reduce test code duplication and test-specific code within production.

Tagged with:  

Cohesion and Controller Ontology

On May 31, 2011, in Uncategorized, by derekgreer

In my article: Single Action Controllers with ASP.Net MVC, I presented a simple way of designing controllers within the ASP.Net MVC framework that represent discrete actions as opposed to classes which contain actions.  This prompted a few questions about the motivations behind this strategy which this article will discuss in more detail. 


To explain, we’ll review the topics of Cohesion, Role Stereotypes and the Single Responsibility Principle.

Cohesion

Cohesion is defined as the functional relatedness of the elements of a module.  If all the methods on a given object pertain to a related set of operations, the object can be said to have high-cohesion.  If an object has a bunch of miscellaneous methods which deal with unrelated concerns, it could be said to have low-cohesion.  Cohesion can be further broken down into categories based upon how the responsibilities are related.  The responsibilities of a given class may be similar in the sequence of operations they perform, in the dependencies they use, in the business domain concerns they facilitate, etc.  What makes an object cohesive depends upon which aspect of relatedness you are measuring. 

Role Stereotypes

Role Stereotypes concern the ontology of an object within object-oriented design.  Role stereotypes can be thought of as role patterns which help us to rationalize the assignment of roles and responsibilities to objects within an architecture.  One set of stereotypes set forth by Rebecca Wirfs-Brock is as follows:

  • Information holder – an object designed to know certain information and provide that information to other objects.
  • Structurer – an object that maintains relationships between objects and information about those relationships.
  • Service provider – an object that performs specific work and offers services to others on demand.
  • Controller – an object designed to make decisions and control a complex task.
  • Coordinator – an object that doesn’t make many decisions but, in a rote or mechanical way, delegates work to other objects.
  • Interfacer – an object that transforms information or requests between distinct parts of a system.

Another set of role stereotypes set forth by Steve Freeman and Nat Pryce as a heuristic for thinking about object roles within the context of collaboration is as follows:

  • Dependencies – Services that the object requires from its peers so it can perform its responsibilities.
  • Notifications – Peers that need to be kept up to date with the object’s activity.
  • Adjustments – Peers that adjust the object’s behavior to the wider needs of the system.

Object stereotypes serve as an aid when considering what roles and responsibilities a particular object should have within a given context, but they shouldn’t be looked to as a set of rules governing how to design.  Depending upon what aspect of a design is being considered, objects can serve in different contexts or may fit multiple stereotypes simultaneously.  Additionally, stereotypes should not be confused with the responsibilities and characteristics prescribed by formal patterns and pattern participants which may happen to share similar naming.  For example, Wirfs-Brock’s Controller  stereotype shouldn’t be confused with Controllers described by the Model-View-Controller, Application Controller or Supervising Controller patterns.

Single Responsibility Principle

The Single Responsibility Principle states:

A class should have only one reason to change.

The Single Responsibility Principle seeks to achieve cohesion by grouping concerns based upon the forces which cause them to change together.  For example, consider an object whose purpose is to provide caching services to an application.  It may have different behaviors for adding, retrieving, and removing items from a cache, but apart from each of these methods being functionally related (i.e. dealing with cache), they would also tend to change together based upon modifications to the underlying caching store implementation (e.g. changing the service to use a caching provider Strategy vs. a local dictionary field).  If the caching service were extended to also encrypt the cached items, this could be reasoned to violate the Single Responsibility Principle.  While the operations may be cohesive from the perspective of the responsibilities which are uniformly applied to the caching of items, persistence and encryption represent two different axes of change.

While the concept of Cohesion and the Single Responsibility Principle are related, something can be considered cohesive by one measure of relatedness, but have multiple reasons to change.  The Single Responsibility Principle measures one type of cohesion: how related the responsibilities of a module are with respect to their axes of change.

Confluence of Principles

So, how do all these abstract concepts relate to how one might decide to implement the Model-View-Controller pattern?  Going back to Trygve Reenskaug’s original design of the Model-View-Controller pattern, the Model’s role was to represent real world processes and entities; the View’s role was to provide the visualization of the model; the Controller’s role was to serve as an interface between the end user and the model.  The Controller’s responsibilities were achieved by intercepting hardware signals in the form of keyboard and mouse events and translating them to operations upon the Model.  As operations upon the Model changed its state, the View would be updated through the use of the Observer Pattern.  The Controller’s role in interacting with the View was primarily presentation-specific concerns (navigation, the expanding and collapsing of menus, highlighting, etc.).  As the MVC pattern was adapted for use with Web applications, the hardware signals were replaced by HTTP requests and the Controller took a more active role in communicating updates to the View due to the stateless nature of the Web.  Though the Controller had to step up its responsibility a bit in this new manifestation of the MVC pattern, its purpose was still that of Model interface.

While the Controller’s primary responsibility is to interpret signals and adapt those signals to meaningful operations upon the Model, the cohesiveness of the operations upon the Controller should be guided by the Single Responsibly Principle within the context of its role stereotype.  That is to say, the role of the Controller within the Model-View-Controller pattern is not to mirror the Model’s responsibility in terms of HTTP requests and responses, but to serve as an adaptor whose responsibilities are organized based upon the relatedness of the technical needs to serve in this adapting role.  For example, a single Domain object or service may have a number of operations which are grouped based upon their relatedness in representing a domain concept, but invoking the behavior of each may require that completely disparate dependencies be supplied to the Model.  One may require an auditing service, another a discount strategy, and still another a caching service.  These represent three separate axes of change which could affect the Controller for different reasons.  The types of requests and responses represent yet another axis of change, as some requests utilize different protocols (e.g. delegation to a View template engine, JSON, binary stream, etc.)

For simple applications, a single controller may adhere to the Single Responsibility Principle due to the singularity of protocol and dependencies required for the underlying Model it is tasked with interfacing (e.g. basic CRUD applications).  As applications grow in complexity, more forces of change begin to be imposed upon the design.  Therefore, applying the same sets of principles will lead you to different manifestations of Controllers depending on the style of application you are designing.

Conclusion

In summary, Cohesion is a measure of the relatedness of the elements of a module, but it is only until we provide context to an element’s purpose that we can begin to define cohesion in a meaningful way.  By identifying the role stereotype of a component, we can then apply the Single Responsibility Principle to the behaviors of the role based upon the cohesiveness of each element’s reason for change.

Tagged with:  

Effective Tests: Auto-mocking Containers

On May 31, 2011, in Uncategorized, by derekgreer
This entry is part 13 of 17 in the series Effective Tests

In the last installment, I set forth some recommendations for using Test Doubles effectively. In this article, I’ll discuss a class of tools which can aid in reducing some of the coupling and obscurity that comes with the use of Test Doubles: Auto-mocking Containers.

Auto-mocking Containers

Executable specifications can provide valuable documentation of a system’s behavior. When written well, they can not only clearly describe what the system does, but also serve as an example for how the system is intended to be used. Unfortunately, it is this aspect of our specifications which can often end up working against our goal of writing maintainable software.

Ideally, an executable specification would describe the expected behavior of a system in such a way as to also clearly demonstrate it’s intended use without obscuring its purpose with extraneous implementation details. One class of tools which aid in achieving this goal are Auto-mocking Containers.

An Auto-mocking Container is a specialized inversion of control container for constructing a System Under Test with Test Doubles automatically supplied for any dependencies. By using an auto-mocking container, details such as the declaration of test double fields and test double instantiation can be removed from the specification, rendering a cleaner implementation void of such extraneous details.

Consider the following class which displays part details to a user and is responsible for retrieving the details requested form a cached copy if present:

 public class DisplayPartDetailsAction
    {
        readonly ICachingService _cachingService;
        readonly IPartDisplayAdaptor _partDisplayAdaptor;
        readonly IPartRepository _partRepository;

        public DisplayPartDetailsAction(
            ICachingService cachingService,
            IPartRepository partRepository,
            IPartDisplayAdaptor partDisplayAdaptor)
        {
            _cachingService = cachingService;
            _partRepository = partRepository;
            _partDisplayAdaptor = partDisplayAdaptor;
        }

        public void Display(string partId)
        {
            PartDetail details = _cachingService.RetrievePartDetails(partId) ??
                                 _partRepository.GetPartDetailByPartId(partId);

            _partDisplayAdaptor.Display(details);
        }
    }

The specification for this behavior would need to verify that the System Under Test attempts to retrieve the PartDetail from the ICachingService, but would also need to supply implementations for the IPartRepository and IPartDisplayAdaptor as shown in the following listing:

    public class when_displaying_part_details
    {
        const string PartId = "12345";
        static Mock<ICachingService> _cachingServiceMock;
        static DisplayPartDetailsAction _subject;

        Establish context = () =>
            {
                _cachingServiceMock = new Mock<ICachingService>();
                var partRepositoryDummy = new Mock<IPartRepository>();
                var partDisplayAdaptorDummy = new Mock<IPartDisplayAdaptor>();
                _subject = new DisplayPartDetailsAction(_cachingServiceMock.Object, partRepositoryDummy.Object,
                                                        partDisplayAdaptorDummy.Object);
            };

        Because of = () => _subject.Display(PartId);

        It should_retrieve_the_part_information_from_the_cache =
            () => _cachingServiceMock.Verify(x => x.RetrievePartDetails(PartId), Times.Exactly(1));
    }

By using an auto-mocking container, the specification can be written without the need of an explicit Mock field, or instantiating Dummy instances for the IPartRepository and IPartDisplayAdaptor dependencies. The following demonstrates such an example using AutoMock, an auto-mocking container which leverages the Moq framework:

    public class when_displaying_part_details
    {
        const string PartId = "12345";
        static AutoMockContainer _container;
        static DisplayPartDetailsAction _subject;

        Establish context = () =>
            {
                _container = new AutoMockContainer(new MockFactory(MockBehavior.Loose));
                _subject = _container.Create<DisplayPartDetailsAction>();
            };

        Because of = () => _subject.Display(PartId);

        It should_retrieve_the_part_information_from_the_cache =
            () => _container.GetMock<ICachingService>().Verify(x => x.RetrievePartDetails(PartId), Times.Exactly(1));
    }

While this implementation eliminates references to the extraneous dependencies, it does impose a bit of extraneous implementation details of its own. To further relieve this specification of implementation details associated with the auto-mocking container, a reusable base context can be extracted:

    public abstract class WithSubject<T> where T : class
    {
        protected static AutoMockContainer Container;
        protected static T Subject;

         Establish context = () =>
            {
                Container = new AutoMockContainer(new MockFactory(MockBehavior.Loose));
                Subject = Container.Create<T>();
            };

        protected static Mock<TDouble> For<TDouble>() where TDouble : class
        {
            return Container.GetMock<TDouble>();
        }
    }

By extending the auto-mocking base context, the specification can be written more concisely:

    public class when_displaying_part_details : WithSubject<DisplayPartDetailsAction>
    {
        const string PartId = "12345";

        Because of = () => Subject.Display(PartId);

        It should_retrieve_the_part_information_from_the_cache =
            () => For<ICachingService>().Verify(x => x.RetrievePartDetails(PartId), Times.Exactly(1));
    }

Another advantage gained by the use of auto-mocking containers is decoupling. By inverting the concern of how the System Under Test is constructed, dependencies can be added, modified, or deleted without affecting specifications for which the dependency has no bearing.

Trade-offs

While auto-mocking containers can make specifications cleaner, easier to write, and more adaptable to change, their use can come at a slight cost. By using mocking frameworks and hand-rolled doubles directly, there is always at least one point of reference where the requirements of instantiating the System Under Test provides feedback about its design as a whole.

Use of auto-mocking containers allows us to produce contextual slices of how the system works, limiting the information about the system’s dependencies to that knowledge required by the context in question. From a documentation perspective, this can aid in understanding how the system is used to facilitate a particular feature. From a design perspective, however, their use can eliminate one source of feedback about the evolving design of the system. Without such inversion of control, hints of violating the Single Responsibility Principle can be seen within the specifications, evidenced by overly complex constructor initialization. By removing the explicit declaration of the system’s dependencies from the specifications, we also remove this point of feedback.

That said, the benefits of leveraging auto-mocking containers tend to outweigh the cost of removing this point of feedback. Cases of mutually-exclusive dependencies are usually in the minority and each addition and/or modification to a constructor provides an equal level of feedback about a class’s potential complexity.

Conclusion

In this article, we looked at the use of auto-mocking containers as a tool for reducing obscurity and coupling within our specifications. Next time, we’ll look at a technique for reducing the obscurity that comes from overly complex assertions.