Coding

What’s New in CompositionTests 2.0

Download the latest version of CompositionTests from nuget.org!

ApprovalTests 3.0

Updated the dependency on ApprovalTests to 3.0.01. Thanks to the new version updating policy for ApprovalTests, CompositionTests should remain forward compatible with future versions of ApprovalTests, unless there are breaking changes in the API.

New version policy

Following LLewellyn’s lead with ApprovalTests, I am adopting a JSON.NET-style version update policy. Adopting this policy will enable me to sign CompositionTests in the future without creating forward-compatibility problems for anyone else. For now, the package remains unsigned because its other dependency, the MEFX Diagnostic Library is unsigned. I’ll have to decide if I’m willing to do anything about that before I can consider a signed version of CompositionTests.

The impact is that the CompositionTests AssemblyVersion will stay at 2.0.0 from now on. The real version can be found by looking at AssemblyFileVersion, or by looking at the nuget package version, which will be 2.0.1 for this release.

Common Language Specification Compliance

The CompositionTests library now declares itself CLS compliant. However, MEFX.Core does not make the same declaration, so certain methods that interact with the core are individually marked non-compliant. I don’t think that MEFX.Core uses anything non-compliant, the library is simply missing the declaration of compliance. I don’t think Microsoft has plans to provide any more updates to this piece, so I’ll have to decide that I’m willing to modify and maintain a fork of MEFX.Core before I can do anything about that missing attribute.

Removed Obsolete Methods

Methods and types marked with the ObsoleteAttribute in the 1.0 time-frame have been removed in order to clean up the interface in 2.0. You must now migrate to Verify* and MefComposition if you wish to use new versions of the library.

Advertisements
Presentations

From "MEF, WTF?!" to "MEF, FTW!"

In addition to my talk on CoffeeScript at SoCalCodeCamp I also reprised my talk on testing and debugging MEF. I didn’t record this session at UCSD but if you missed it, you can watch a recording of the “premiere” at CSU Fullerton. While I covered a lot of the same ground at UCSD, I did revise the slides and rewrote the sample code from scratch. The biggest difference is that at UCSD I introduced CompositionTests at the end of the talk, which I hope lowers the barrier of entry for beginners.

This post should be short because I’ve already written a few posts on this topic. However, if you attended the session and are here looking for additional information, here’s a list of resources:

Since I don’t have a video, here’s a picture of me standing in front of a Pizza Factory.  See, it’s more that just a lame example from a design patterns book.

WP_000324

A Few Comments on the Sample Code

As I mentioned I rewrote the code. Mostly because the old code was way too complicated. For some reason I was obsessed with using DirectoryCatalogs for everything during the first six months of my career with MEF. In the last six months I’ve come to appreciate the other catalogs, especially while refactoring and certainly for demos. This helped simplify the code a great deal.

Having the benefit of giving the talk before, I knew that I didn’t have time to cover all the failure scenarios I programmed into the original demo. I think I had about 8 different scenarios in the original PizzaStore, and in reality there is only time to demo about two. So, while rewriting the code, I only setup two scenarios, one failure that doesn’t throw an exception, and one that does.

To demonstrate the difference between .NET 4 and 4.5, I used a separate solution. The first time around I copied the solution folder, renamed it, changed references and I was rockin’ and rollin’. This worked well enough to Demo, but the truth was that only one line of code needed to change between .NET 4 and 4.5. This time around the projects share the same files using the “Add As Link” option in Visual Studio.

If you want to poke around the code, you should know that the “Program” project does almost nothing. It holds part definitions that the “Demo” project can play with. The Program class, while it does nothing useful, does show an example of my preferred way to share ExportProviders and ComposablePartCatalogs with test code.

Over in the Demo project we use the TypeCatalog instances to control whether the composition succeeds or fails. We also have an example of implementing a composition test by hand, which includes some really ugly brute force formatting necessary to ensure that the parts are listed in the same order every time. Finally, we have an example of composition test using my own CompositionTests library, which not only removes the burden of writing broiler plate code, but handles the ordering for you.

Coding, Testing

When is the ExportProvider Interesting?

Recently, when writing about the CompositionTests API (here) I wrote that our test should focus on the interesting part of the composition and that most of the time the catalog is the only interesting item.  Later I wrote that there were other scenarios where the ExportProvider was also interesting and when I wrote that I was thinking of custom ExportProvider implementations.  Just Google “mef custom exportprovider” and you will find a wealth of articles written by people who created their own ExportProviders to adapt MEF for their specific needs.  I think I even built one back when I first started out with MEF.

But lets say you haven’t needed or wanted to roll your own ExportProvider.  Does trusty old CompositionContainer ever effect composition?  Can it ever be interesting enough to motivate you to use VerifyCompositionInfo?

A Simple Mailer

Here’s an example of when you might want to use VerifyCompositionInfo because the CompositionContainer is interesting.  Say you are working on an application that wants to send emails. You might start with a little mail sender like this:

[Export(typeof(ISendMail))]
public class BasicMailSender : ISendMail
{
    [ImportingConstructor]
    public BasicMailSender(
        SmtpHost host,
        Port port)
    {
        this.port = port;
        this.host = host;
    }

    public void SendMessage(MailMessage message)
    {
        // ...
    }
}

You have read the CompositionTests readme, and written a simple Program class that allows you to share your ComposablePartCatalog (in this case a TypeCatalog) with your test.

public class Program
{
    static Program()
    {
        Catalog = new TypeCatalog(typeof(BasicMailSender));
        Host = new CompositionContainer(Catalog);
    }

    public static TypeCatalog Catalog { get; private set; }

    public static CompositionContainer Host { get; private set; }

    public static void Main(string[] args)
    {
    }
}

And written a test.

using ApprovalTests.Reporters;
using CompositionTests;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace MailApp.Tests
{
    [TestClass]
    [UseReporter(typeof(DiffReporter))]
    public class CompositionTest
    {
        [TestMethod]
        public void VerifyComposition()
        {
            MefComposition.VerifyTypeCatalog(Program.Catalog);
        }
    }
}

Given our current setup, we would expect an unhappy composition, shown below.

[Part] MailApp.BasicMailSender from: TypeCatalog (Types='MailApp.BasicMailSender').
  [Primary Rejection]
  [Export] MailApp.BasicMailSender (ContractName="MailApp.BasicMailSender")
  [Import] MailApp.BasicMailSender..ctor (Parameter="host", ContractName="MailApp.SmtpHost")
    [Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint: 
    ContractName    MailApp.SmtpHost
    RequiredTypeIdentity    MailApp.SmtpHost
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition)
   at Microsoft.ComponentModel.Composition.Diagnostics.CompositionInfo.AnalyzeImportDefinition(ExportProvider host, IEnumerable`1 availableParts, ImportDefinition id)
  [Import] MailApp.BasicMailSender..ctor (Parameter="port", ContractName="MailApp.Port")
    [Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint: 
    ContractName    MailApp.Port
    RequiredTypeIdentity    MailApp.Port
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition)
   at Microsoft.ComponentModel.Composition.Diagnostics.CompositionInfo.AnalyzeImportDefinition(ExportProvider host, IEnumerable`1 availableParts, ImportDefinition id)

So we need to provide SmtpHost and Port implementations. The decision on the Port implementation is easy. While we might want to use a non-default Port during testing, we feel pretty safe determining that we want to use the default SMTP port (25) in production. So we can whip up a DefaultPort implementation and export it.

[Export(typeof(Port))]
public class DefaultPort : Port
{
    public DefaultPort() :
        base(25)
    {
    }
}

Everything works as expected if we update our TypeCatalog with this new type.

static Program()
{
    Catalog = new TypeCatalog(typeof(BasicMailSender), typeof(DefaultPort));
    Host = new CompositionContainer(Catalog);
}

And now the test produces this result:

[Part] MailApp.BasicMailSender from: TypeCatalog (Types='MailApp.BasicMailSender, MailApp.DefaultPort').
  [Primary Rejection]
  [Export] MailApp.BasicMailSender (ContractName="MailApp.BasicMailSender")
  [Import] MailApp.BasicMailSender..ctor (Parameter="host", ContractName="MailApp.SmtpHost")
    [Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint: 
    ContractName    MailApp.SmtpHost
    RequiredTypeIdentity    MailApp.SmtpHost
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition)
   at Microsoft.ComponentModel.Composition.Diagnostics.CompositionInfo.AnalyzeImportDefinition(ExportProvider host, IEnumerable`1 availableParts, ImportDefinition id)
  [Import] MailApp.BasicMailSender..ctor (Parameter="port", ContractName="MailApp.Port")
    [SatisfiedBy] MailApp.DefaultPort (ContractName="MailApp.Port") from: MailApp.DefaultPort from: TypeCatalog (Types='MailApp.BasicMailSender, MailApp.DefaultPort').

[Part] MailApp.DefaultPort from: TypeCatalog (Types='MailApp.BasicMailSender, MailApp.DefaultPort').
  [Export] MailApp.DefaultPort (ContractName="MailApp.Port")

The Port is now satisfied, but SmtpHost is trickier. While the default port for SMTP is defined by standard, there is no such thing as a default server. Now, I would note that one option is to simply approve this result. There is no rule that says you can only approve a CompositionTest when it has no exceptions. The fact that SmtpHost can’t be satisfied could just mean that this part must be provided by a third-party at deployment time. However, there are two reasons why I’ll try to satisfy all of the parts in this test.

  • Ignoring exceptions is like ignoring warnings. When there are only a few you can say to yourself, “Well, that one is there for a reason, and I understand why.” But when the number of warnings/exceptions grow, the signal to noise ratio rises and eventually obscures real errors.
  • If I don’t try to satisfy the part, I won’t be able to show you a scenario where the ExportProvider is interesting.

Lets say that we have a SmtpHost in our test project that we use for development, it points at the localhost. When making these kind of test collaborators I usually nest the Testing class inside the test it supports.

[TestClass]
[UseReporter(typeof(DiffReporter))]
public class CompositionTest
{
    [TestMethod]
    public void VerifyComposition()
    {
        MefComposition.VerifyTypeCatalog(Program.Catalog);
    }

    [Export(typeof(SmtpHost))]
    public class TestingSmtpHost : SmtpHost
    {
        public TestingSmtpHost()
            : base("localhost")
        {
        }
    }
}

It would be nice to use this part when verifying composition, but since it is in our test class, our production code can’t see it. One way around this is to add an instance to our CompositionContainer using a CompositionBatch. We’ll need to add a method, Program.AddInstance, to allow this.

public class Program
{
    static Program()
    {
        Catalog = new TypeCatalog(typeof(BasicMailSender), typeof(DefaultPort));
        Host = new CompositionContainer(Catalog);
    }

    public static TypeCatalog Catalog { get; private set; }

    public static CompositionContainer Host { get; private set; }

    public static void AddInstance(object part)
    {
        var batch = new CompositionBatch();
        batch.AddPart(part);
        Host.Compose(batch);
    }

    // ...
}

Now we can add a class constructor to our test, so that Program is only manipulated once during the course of the test.

[TestClass]
[UseReporter(typeof(DiffReporter))]
public class CompositionTest
{
    static CompositionTest()
    {
        Program.AddInstance(new TestingSmtpHost());
    }

Note that you can’t just throw any object into the method and expect it to compose, it has to be an attributed MEF part. Ok, now we might expect our test result to show a happy composition, but the BasicMailSender still fails to compose.

[Import] MailApp.BasicMailSender..ctor (Parameter="host", ContractName="MailApp.SmtpHost")
  [Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint: 
    ContractName    MailApp.SmtpHost
    RequiredTypeIdentity    MailApp.SmtpHost

What’s up with that? Look closely at the AddInstance method. Nowhere in that method do we manipulate the Catalog.  When we use CompositionBatch, we are adding part directly to the CompositionContainer–outside the catalog. So, our ExportProvider (in this case just a plain old CompositionContainer) is suddenly interesting. Without it, there is no way for the CompositionInfo used by CompositionTests to know about the instance of TestingSmtpHost that we added.

Lets rewrite our test to use an overload of VerifyCompositionInfo so that we can pass in the ExportProvider.

[TestMethod]
public void VerifyComposition()
{
    MefComposition.VerifyCompositionInfo(Program.Catalog, Program.Host);
}

And now our composition is happy.

[Part] MailApp.BasicMailSender from: TypeCatalog (Types='MailApp.BasicMailSender, MailApp.DefaultPort').
  [Export] MailApp.BasicMailSender (ContractName="MailApp.BasicMailSender")
  [Import] MailApp.BasicMailSender..ctor (Parameter="host", ContractName="MailApp.SmtpHost")
    [SatisfiedBy] MailApp.Tests.CompositionTest+TestingSmtpHost (ContractName="MailApp.SmtpHost") from: MailApp.Tests.CompositionTest+TestingSmtpHost
  [Import] MailApp.BasicMailSender..ctor (Parameter="port", ContractName="MailApp.Port")
    [SatisfiedBy] MailApp.DefaultPort (ContractName="MailApp.Port") from: MailApp.DefaultPort from: TypeCatalog (Types='MailApp.BasicMailSender, MailApp.DefaultPort').

[Part] MailApp.DefaultPort from: TypeCatalog (Types='MailApp.BasicMailSender, MailApp.DefaultPort').
  [Export] MailApp.DefaultPort (ContractName="MailApp.Port")

Notice that while the TestingSmtpHost satisfies the host parameter on the BasicMailSender constructor, its “from” tag doesn’t indicate that it comes from a catalog (in contrast, you can see that DefaultPort came from the TypeCatalog). Likewise, TestingSmtpHost has no [Part] listing and a [Part] listing for DefaultPort does exist.

Most of the time, you can analyze your composition using just the catalog, but the container can have parts too. When your container has parts, that’s when you’ll need to use VerifyCompositionInfo directly to get the full analysis.

You can download and play with the MailApp code from the Sample directory in the CompositionTests github repository.

Coding, Testing

MEF Composition Tests, Redux

Photo Credit: FutUndBeidl

A while back I gave a talk at SoCalCodeCamp about testing MEF composition.  Because there are a few steps involved in setting up the test, I wrote Stop Guessing About MEF Composition And Start Testing.  I had hoped that the article, along with the source code from my talk would help more people cross the gap from having once heard that it was possible to test composition, to actually implementing tests.  Since there were only about five people at my talk, it didn’t take long for the number of blog readers to exceed the number of attendees, and yet I still wonder how many people decide setting up the test is too complicated and just decide to use MEFX or Visual MEFX “as needed”.

While MEFX tools are valuable (and these composition tests are just a more powerful way to automate MEFX), running tests on an ad-hoc basis is never ideal.  Bugs creep in when you don’t expect them to, and automatic tests are your defense against them.  I also find composition tests provide valuable design-time feedback.  While taking questions at the end of the talk, I was asked about my process for designing MEF parts.  I answered that I more or less disregarded MEF until the end of the process, and then just added the needed attributes.  That was a truthful answer, but over the intervening months, I’ve realized it was the wrong answer.  What finally put the nail in the coffin was this great article: How test-driven development works (and more!), by J. B. Rainsberger.  After reading, it hit me that that I had setup a little waterfall process.  First, design and build a unit.  Then, glue MEF onto it.  The problem is that even when “keeping MEF in mind” I would sometimes do foolish things like pass a bunch of primitives into a constructor, and this would lead me to rework at the end of the development cycle when I tried to shove my class into a container.

So, to avoid rework and break some bad habits, I decided to start testing composition to get feedback during the design phase as soon as I know I’m going to use MEF (which these days is most of the time).  Its already paying dividends on my current project, as soon as I see the need to pass in a simple piece of data like a string or an integer, my composition test will blow up if I pass it through the constructor.  A lot of times I still prefer to pass the data in through the constructor, but since my test is blowing up, I’m forced design and test the system for getting that data to the constructor immediately, rather than at the end of the cycle.

I started testing composition out of necessity, I had a broken composition that needed fixing.  Now, I do it to save time and effort, and it seems more valuable to me every day.  More people should do it, but I’m afraid some will be turned off by the number of steps and concepts involved.

Here are a few of them:

  1. Synchronizing catalogs between test and production
  2. For some, this could also be their first exposure to ApprovalTests
  3. Finding a copy of MEFX.
  4. Setting up the test.

So, does it have to be so complicated?

Reducing the Surface Area

Lets look at a sample project and implement the integration test I described in my last article about composition tests.  Some people have a one-track mind.  Mine has two tracks: cars and pizza.  This time we will go with cars and our sample project will be a simple program called CarDealership with some parts related to cars in it.  It doesn’t have to be very complex since our primary interest is the test.

Lets look at that test:

[TestClass]
public class IntegrationTest
{
  [TestMethod]
  public void DiscoverParts()
  {
    try
    {
      var catalog = new DirectoryCatalog(".");
      var host = new CompositionContainer(catalog);
      var compositionInfo = new CompositionInfo(catalog, host);
      using (var stringWriter = new StringWriter())
      {
        CompositionInfoTextFormatter.Write(
            compositionInfo,
            stringWriter);
        Approvals.Verify(stringWriter.ToString());
      }
    }
    catch (ReflectionTypeLoadException ex)
    {
      Array.ForEach(
          ex.LoaderExceptions,
          lex => Console.WriteLine(lex.ToString()));
      throw;
    }
  }
}

A few things are already different about the test.  There’s no UseReporterAttribute on the class anymore, because I usually define this in AssemblyInfo.cs now using an assembly scoped attribute:

// AssemblyInfo.cs
using ApprovalTests.Reporters;
[assembly: UseReporter(typeof(DiffReporter))]

I also moved a few more lines inside the test body because I don’t usually have a reason to run more than one test with the same CompositionInfo.  Also, since my intention is to break this test down into something cleaner, I want to see it all in one place.

Right now, our test wont compile because we don’t have references to ApprovalTests or MEFX.  ApprovalTests has been available on NuGet for some time, and lately the NuGet package has been kept up to date with releases on SourceForge.  So, go ahead and use NuGet to install ApprovalTests:

In my previous post I explained where to find MEFX, which includes the CompositionDiagnostics library that we need for our test.  But you had to download it or compile it, and then add the reference by hand… icky.  But, we had no choice because this library wasn’t available on NuGet.  Well, it is available now, because I put it there a few months ago.  Now its much easier to get your hands on a copy of the library:

image

Now our test compiles and runs.  While you weren’t looking I added some parts to the CarDealership program.  But it doesn’t matter because I haven’t setup a pre-build event to copy assemblies from production to test.

I never liked doing that.  I knew it would be brittle and it was.  Ideally, I’d just like to use the exact catalog from production in my test.  It turns out this is a lot easier to arrange if you don’t use a DirectoryCatalog.  AssemblyCatalogs or TypeCatalogs are easy to share programmatically.  So, ask yourself if you really need to use a DirectoryCatalog.  Lets see what the test looks like when we use an AssemblyCatalog:

[TestMethod]
public void DiscoverParts()
{
  try
  {      
    var compositionInfo = new CompositionInfo(
      Program.Catalog, 
      Program.Host);
    using (var stringWriter = new StringWriter())
    {
      CompositionInfoTextFormatter.Write(
          compositionInfo,
          stringWriter);
      Approvals.Verify(stringWriter.ToString());
    }
  }
  catch (ReflectionTypeLoadException ex)
  {
    Array.ForEach(
        ex.LoaderExceptions,
        lex => Console.WriteLine(lex.ToString()));
    throw;
  }
}

To facilitate sharing, I’ve added static properties on the Program class.  They are simple properties, initialized in Program’s class constructor:

using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Reflection;

namespace CarDealership
{
  public class Program
  {
    static Program()
    {
      Catalog = new AssemblyCatalog(Assembly.GetAssembly(typeof(Program)));
      Host = new CompositionContainer(Catalog);
    }

    public static ComposablePartCatalog Catalog { get; private set; }

    public static ExportProvider Host { get; private set; }

    private static void Main(string[] args)
    {
    }
  }
}

Looking back at the test, we can see the effect of sharing the catalog and host from Program.  We can now get the CompositionInfo with just one statement:

var compositionInfo = new CompositionInfo(Program.Catalog, Program.Host);

Another interesting development, the catalog and host are only referenced on this line.  Could we create a method that takes “compositionInfo” as a parameter without changing this test’s behavior?  The answer is no, but lets try it and see why.

[TestMethod]
public void DiscoverParts()
{
  var compositionInfo = new CompositionInfo(
    Program.Catalog,
    Program.Host);
  DiscoverParts(compositionInfo);
}

private static void DiscoverParts(CompositionInfo compositionInfo)
{
  try
  {
    using (var stringWriter = new StringWriter())
    {
      CompositionInfoTextFormatter.Write(
          compositionInfo,
          stringWriter);
      Approvals.Verify(stringWriter.ToString());
    }
  }
  catch (ReflectionTypeLoadException ex)
  {
    Array.ForEach(
        ex.LoaderExceptions,
        lex => Console.WriteLine(lex.ToString()));
    throw;
  }
}

To make the CompositionInfo a parameter, we pulled it out of the try-catch block.  Since the CompositionInfo constructor can throw the ReflectionTypeLoadException we’re trying to catch, moving it out of the try-catch block defeats the purpose of the exception handler.  Moving the exception handler back to the test method requires us to implement the handler (which never changes) every time we write a test.  We need to control when “new” is called on CompositionInfo.  We can do so by changing our parameter into a Func<CompositionInfo>:

[TestMethod]
public void DiscoverParts()
{
  DiscoverParts(() => new CompositionInfo(Program.Catalog, Program.Host));
}

private static void DiscoverParts(Func<CompositionInfo> getCompositionInfo)
{
  try
  {
    using (var stringWriter = new StringWriter())
    {
      CompositionInfoTextFormatter.Write(
          getCompositionInfo(),
          stringWriter);
      Approvals.Verify(stringWriter.ToString());
    }
  }
  catch (ReflectionTypeLoadException ex)
  {
    Array.ForEach(
        ex.LoaderExceptions,
        lex => Console.WriteLine(lex.ToString()));
    throw;
  }
}

Now, I could still create a CompositionInfo instance in the test method, then use the delegate to pass back that instance.  Doing so, I would not be protected by the exception handler, but that would be a minor problem.  The exception handler only exists as a time saver, when a ReflectionTypeLoadException occurs, it will print additional information not included in the default dump.  Without the handler, you would need to rerun the test in the debugger in order to see the loader exceptions.  So, its not the end of the world, but at least now its possible to reuse the exception handler as long as we remember how to build the delegate correctly.

Can we make DiscoverParts easier to use by removing the caveat that the constructor must be part of the delegate?  We could provide an overload that creates the delegate for the caller:

[TestMethod]
public void DiscoverParts()
{
  DiscoverParts(Program.Catalog, Program.Host);
}

private static void DiscoverParts(ComposablePartCatalog catalog, ExportProvider host)
{
  DiscoverParts(() => new CompositionInfo(catalog, host));
}

private static void DiscoverParts(Func<CompositionInfo> getCompositionInfo)
{
   // ...
}

That’s better, and maybe we don’t need that delegate after all.  For the time being, I’m going to leave it there because I think the more interesting question is: Do we need the host?  If you look again at the construction of the host in Program, you’ll see that it’s the simplest possible CompositionContainer, and the only interesting thing about it is the catalog it consumes.  So why not focus our test on the catalog?

[TestMethod]
public void DiscoverParts()
{
  DiscoverParts(Program.Catalog);
}

private static void DiscoverParts(ComposablePartCatalog catalog)
{
  DiscoverParts(catalog, new CompositionContainer(catalog));
}

private static void DiscoverParts(ComposablePartCatalog catalog, ExportProvider host)
{
  DiscoverParts(() => new CompositionInfo(catalog, host));
}

In a great many cases, the container itself is very boring, but there certainly are plenty of cases where some other, more interesting, ExportProvider comes into play. So, both overloads are useful.  In the case where only the catalog is interesting, the overload without the host should be used.  This sends the signal that the host is not important.  On the other hand, if you choose to use the overload with the host specified, then you’re sending the signal that the host is interesting and important.  Who are you sending the signal too?  Probably your future self.

So we’ve accomplished a lot.  Our test method has been reduced to a simple method call that can take as little as one parameter.  The best thing about it is that this parameter is part of the “normal’ set of Composition types, it does not require the caller to know anything about CompositionInfo, CompositionInfoTextFormatter, or ReflectionTypeLoadException.  All that code is still there though, and its not conveniently packaged for distribution.  Can we make this test even easier by completely hiding all this code?

Introducing CompositionTests

One could take everything we’ve looked at here, bundle it up into a library, and put it on NuGet and GitHub.  I’ve done just that:

image

It’s just a little library but it should make composition tests dead simple:

using CompositionTests;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CarDealership.Tests
{
  [TestClass]
  public class IntegrationTest
  {
    [TestMethod]
    public void DiscoverParts()
    {
      Composition.DiscoverParts(Program.Catalog);
    }
  }
}

That’s the whole test.

You can also scrub the formatted text before sending it to ApprovalTests.

using ApprovalUtilities.Utilities;
using CompositionTests;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CarDealership.Tests
{
  [TestClass]
  public class IntegrationTest
  {
    [TestMethod]
    public void DiscoverParts()
    {
      Composition.DiscoverParts(
        Program.Catalog,
        StringExtensions.ScrubVersionNumber,
        s => s.ScrubPath(@"C:\Test"));
    }
  }
}

You can use any number of scrubbers to transform your text into a consistent state.  For example, scrubbing version numbers is useful when you use an AssemblyCatalog with an assembly that has an auto incrementing build number.  I’ve also included an extension to scrub the public key token, which is useful when using NCrunch (which disables assembly signing in its builds) alongside other test runners (which do sign the assemblies).  Or, you can use any Func<string, string> that you can think of.

The library also includes (and uses by default) an text formatter that orders the parts by name.  The default text formatter does not guarantee that the parts come out in any particular order.  I have seen them printed in different orders in different build environments, so enforcing order is a must if we want our tests to play nice with VS, NCrunch and a build server.  I’m still not sure that I’ve covered all the scenarios that can lead to things appearing out of order, because the behavior is not easy to trigger.  Luckily I had an example in production which I was able to recreate in the library’s unit tests.  If I encounter any more ordering problems, they will be addressed in future versions of the library.

Check It Out