Archive for January, 2009

TDD and Commerce Server 2007: Custom Rollback

Wednesday, January 28th, 2009comments

In my previous post I mentioned using a TransactionScope to rollback Commerce Server database updates, when unit testing, may not always work. Turns out it doesn’t work with the profile subsystem.

To verify this, I coded an example similar to the address example on MSDN, resulting in the following test:

[Fact]
public void Should_save_address_changes() {
    var addressId = Guid.NewGuid();
    var addressService = new AddressService(TestProfileContext);
    var address = createNewAddress(addressService, addressId);

    addressService.Save(address);

    var savedAddress = addressService.Find(addressId);
    Assert.Equal(address.Line1, savedAddress.Line1);
    Assert.Equal(address.City, savedAddress.City);
    Assert.Equal(address.State, savedAddress.State);
    Assert.Equal(address.ZipCode, savedAddress.ZipCode);
}

For this test, I extended the CommerceFixture class (created in my previous post) to create a TestProfileContext instance. If this test is executed, it will always save a new address to the Commerce Server Profiles Database. If we try adding an AutoRollback attribute to the test, the new address doesn’t get rolled back.

To fix this we can call TestProfileContext.DeleteProfile when the test completes. An easy way to do this, while making the code exception safe, is to create a scope class that implements IDisposable. This allows us to wrap the test in a using block, reducing the amount of code we need to type. Here’s an example implementation:

public class ProfileScope : IDisposable {
    private readonly ProfileContext _profileContext;
    private readonly string _keyValue;
    private readonly string _profileType;

    public ProfileScope(ProfileContext context, string keyValue, string profileType) {
        _profileContext = context;
        _keyValue = keyValue;
        _profileType = profileType;
    }

    public void Dispose() {
        _profileContext.DeleteProfile(_keyValue, _profileType);
    }
}

Now we can update the test, adding a using block with our scope object:

[Fact]
public void Should_save_address_changes() {
    //arrange

    using (new ProfileScope(TestProfileContext, addressId.ToString(),
        AddressService.ProfileName)) {

        addressService.Save(address);

        //assert
    }
}

We can trim the code down even further by adding a helper method to the base test class, CommerceFixture:

public class CommerceFixture {
    ...
    public static ProfileScope ProfileScope(string keyValue, string profileType){
        return new ProfileScope(TestProfileContext, keyValue, profileType);
    }
    ...
}

Which gives us our final version:

[Fact]
public void Should_save_address_changes() {
    //arrange

    using (ProfileScope(addressId.ToString(), AddressService.ProfileName)) {
        addressService.Save(address);

        //assert
    }
}

You can download the code for this example here.

Posted in CommerceServer, TDD

One tough pest

Monday, January 26th, 2009comments

The saga of moving back to a single Vista64 boot from a dual with ArchLinux…

First treatment

Vista System Recovery Options
-> Startup Repair -> Success
-> Reboot ->

GRUB Loading stage 1.5.

GRUB loading, please wait...
Error 22

Second treatment

Vista System Recovery Options
-> Command Prompt
-> bootrec /fixmbr -> The operation completed successfully
-> Reboot ->

GRUB Loading stage 1.5.

GRUB loading, please wait...
Error 22

Third treatment

Vista System Recovery Options
-> Command Prompt
-> bootrec /fixmbr -> The operation completed successfully
-> bootrec /fixboot -> Element not found -> sonofa#?*
-> Reboot ->

GRUB Loading stage 1.5.

GRUB loading, please wait...
Error 22

Panic setting in…

… a light of hope surfaces

Vista System Recovery Options -> Command Prompt
-> copy MbrFix from thumb drive to hard drive (64 bit version of course)
-> cross fingers
-> MbrFix /drive 0 savembr Backup_MBR.bin
-> MbrFix /drive 0 fixmbr /vista /yes
-> Reboot ->

SUCCESS!

Wondering what bootrec /fixmbr is useful for?

Wondering why this page doesn’t have a donation button?

Time wasted: ~4 hours

Posted in Vista

TDD and Commerce Server 2007: Getting Started

Thursday, January 22nd, 2009comments

Mixing Commerce Server 2007 with unit testing frameworks is a little tricky. The few examples I’ve seen use MsTest specific features to route tests through ASP.NET or IIS when executing, providing the following drawbacks:

  1. Increased test execution time (sucks worse when running on a VPC)
  2. Increased cost for creating tests

Techniques like dependency injection can be used to significantly reduce the number of tests that require a web host for execution. I’m using the Orders subsystem here, but the same techniques can be used with other subsystems as well.

Example Code

For this example I unpacked the CSharp.pup file to create a new site named CommerceSample. You can download the example code here.

Breaking Dependencies

Commerce Server is tightly coupled to ASP.NET and this causes problems when trying to execute Commerce Server API code outside of a web application. Suppose we try to execute the following code within a unit testing framework:

var userId = Guid.NewGuid();
var basket = OrderContext.Current.GetBasket(userId);

This produces a NullReferenceException due to an uninitialized singleton, OrderContext.Current. In a web application scenario this context is initialized by the HttpModule CommerceOrderModule.

Establishing a Test Context

To execute code like this within a testing framework, we need to create an instance of OrderContext. One approach is to create a custom base class for tests requiring the use of an OrderContext, as shown below:

public class CommerceFixture {
    public const string SiteName = "CommerceSample";

    public static readonly OrderContext TestOrderContext =
        OrderContext.Create(SiteName);
}

If I have a test that requires an OrderContext, my test class can now extend this class and have access to the TestOrderContext instance. One thing to point out: OrderContext.Create will also initialize OrderContext.Current, so technically you don’t need to store the reference in an accessible property.

Wrapping and Injecting Context Objects

When working with context objects, I like to create wrappper types and inject the context into the wrapper. This allows for the following benefits:

  1. The ability to add new behavior to context objects (the usual benefit of wrapper classes)
  2. No dependence on singletons (a lot of material has been written about this already)
  3. Narrowing the API. Sometimes context objects can be a dumping ground for anything and everything. Wrapper classes provide a nice way to offer a subset of this functionality specific to my application.

Rolling Back Updates

If a test updates a database, we want to make sure the updates are removed upon test completion, preventing unwanted side effects. Most mature testing frameworks provide some form of a rollback attribute, which wraps each test in a TransactionScope instance (if you’re stuck with MsTest, you can wrap your test code in a using TransactionScope block).

Note: Originally I was told using a TransactionScope might not work with Commerce Server API’s due to legacy COM code, so I’ve actually never used this during full project development. I didn’t realize this worked until playing with this example while creating this post. One context type it might not work with is the ProfileContext. That said, there’s more than one way to rollback changes. I’ll discuss the rollback approach I’ve used before in a subsequent post.

An Example

Lets polish the recurring order example from MSDN. I’d like to have a method that accepts a user id and a template name (basket name) and returns a basket that is already populated with items from the saved template:

var cartService = new ShoppingCartService(orderContext);
var cart = cartService.CreateFromRecurringOrder(userId, templateName);

ShoppingCartService is an example of a thin wrapper, where the context is injected in.

To setup a test I need to create a recurring order template in the database, so my service class can find it. This is the update I want rolled back upon test completion. My final test looks like this:

[Fact, AutoRollback]
public void Should_create_new_order_from_recurring_order_for_user() {
    var userId = Guid.NewGuid();
    var templateName = "MonthlyOrder";
    var cartService = new ShoppingCartService(TestOrderContext);

    var recurringOrder = buildRecurringOrder(cartService, userId, templateName);
    var cart = cartService.CreateFromRecurringOrder(userId, templateName);

    Assert.Equal(recurringOrder.LineItems.Count, cart.LineItems.Count);
}

Where buildRecurringOrder is a helper function that creates an order template in the database. For this test I used xUnit.net. When the test completes, the order I saved will no longer exist. You can use the Customer and Orders Manager to verify that your tests are not leaking baskets and orders.

Future Topics

In subsequent posts I’ll discuss the following topics:

  1. Using custom scope objects for situations where TransactionScope might not work
  2. How to handle pipeline execution with testing frameworks
  3. Testing with Commerce Server 2009

Related Information

  1. Dependency Injection
  2. Misko Hevery discussing hard to test third party code
  3. Context object pain. See Misko here
  4. Singleton pain. See Misko again

Posted in CommerceServer, TDD

Always Start When Debugging

Monday, January 12th, 2009comments

On my current project I’m working with MsTest and ASP.NET in Visual Studio 2008. I’ve spent the last few weeks working on building models to support some user controls and most of my time has been spent flushing out models in a test project. Typically I run tests using the Test View window in release mode. I ended up running a specific test in debug mode today when I noticed this:

Developer Web Server starting

Hmmm. Why would the Developer Web Server start when I’m running tests? Maybe there’s an option in Property Pages > Start Options for the site project…nope.

After some digging around I realized I forgot about the other property settings interface, the Properties Window:

VS2008 Properties Window

Changing this to False fixes the problem. It seems odd that the default is True…

Posted in ASP.NET, VS2008

Getting started

Saturday, January 10th, 2009comments

To set this site up I used some feedback from stackoverflow. I’m using DreamHost for hosting and WordPress for publishing. For a base theme, I chose Journalist.

As I make more customizations, I’ll try to keep this page updated with what I’m using.

Posted in About