A DSL for testing cash registers

I’m kicking around an idea for a DSL for testing cash register transactions. My goal is for it to be simple enough that someone without a programming background could verify the test scenarios. To do that, I’m drawing inspiration from something that everyone at my employer is familiar with–receipts. The test input will look like a register receipt and the output will be another receipt with an expected and actual column.

test "Product at retail":
	item		"123"

	subtotal	50.00
	tax		 3.75
	total		53.75

This is just plain text in a file. You can also declare products, tender types, discounts, tax rates, etc in the same file to include them in a test. The entire DSL is declarative. For example, here I’m declaring an item before the discount it uses.

product:
	sku		"234"
	retail		10.00
	discounts	"bogo"

item_discount:
	name		"bogo"
	buy		1
	getFree		1

test "Item with BOGO":
	item		"234", 2    // qty = 2
	
	subtotal	10.00
	tax		 0.75
	total		10.75

The output of these tests is intended to look like another receipt.

Passed “Item at retail”

SKU                         Qty      Total
-------------------- ---------- ----------
123                        1.00      50.00

                       Expected     Actual
                     ---------- ----------
            Subtotal      50.00      50.00
           Discounts       0.00       0.00
               Taxes       3.75       3.75
               Total      53.75      53.75


FAILED “Item with BOGO”

SKU                         Qty      Total
-------------------- ---------- ----------
234                        2.00      20.00 
   bogo                             -10.00

                       Expected     Actual
                     ---------- ----------
            Subtotal      10.00      10.00
           Discounts       0.00       0.00
               Taxes       0.75       1.50 ***  // bug: tax applied on retail price
               Total      10.75      11.50 ***

The ***s indicate lines which failed the expectations. A future extension to the DSL may allow you to set expectations on discounts individually.

To pull this off, I leveraged RhinoDSL from Ayende Rahien. RhinoDSL is a tool for creating DSLs in Boo, a minimalist and very flexible .NET language which plays nicely with C# applications. I started from the OrderDSL sample and added a method for each keyword. When compiled, the tests look more like the following C# code:

public class TestScenario : BaseOrderDSL
{
	public override void Prepare()
	{
		Test("item with bogo", () => {
			Item(“123”, 2);
			Subtotal(10.00m);
			Tax(0.75m);
			Total(10.75m);
		});
	}
}

All the methods called are declared in BaseOrderDSL. The Execute() method provided by the base class transforms the test objects into domain objects and passes them to the application. The receipt output is generated by a bunch of string.Format() calls using the pad left/right string formatters (eg: {1,10:N2}). In all it’s only about 300 lines of code, and most of that is just ceremony for wiring up keywords.

If you’d like to learn more about writing DSLs in .NET applications, I’d recommend picking up a copy of Ayende’s book DSLs in Boo.