Friday, 16 July 2010

testing migration from 0.9-ml to 1.0-ml

Chances are that ever so often you'll come across some legacy XQuery code in 0.9-ml that should really be upgraded to 1.0-ml for the obvious reasons, and everyone seams to be ever so scared of touching the code, because "it works".

So ultimately your duty is to make sure the code operates exactly as it did before. Ideally you should run each function in each module on the legacy code, and on the refactured code, and then compare the output. At the very least you should run tests on entry point functions in your modules.

Now that's easier said than done. If you have followed a TDD methodology from the outset, great, all you have to do is to run the tests against the refactured code, and bob is your oncle, voila . . . ready to go. But as we all know, often the case is that no tests are available for the legacy code, or tests are targeted at abstracted java DAO interfaces, and really don't dive deep enough into the fragments returned by your xquery.

Some of the queries may even return large xml fragments and making it difficult to compare the output visually, and very expensive to use fn:deep-compare().

If you come across one or more of the above scenarios, then here's how I have dealt with it: First I create a sub-folder containing a clone of the original modules in ML, containing the refactured code. Then you need to import both modules into a single test module.

Note that both sets of modules will need different namespaces even if just temporarily. Only then the test module can import both modules also using different namespace prefixes.

You can then run the same functions simultaneously on both the legacy code, and the refactured code. Assign a variable for the outcome of each and wrap the function call in a xdmp:quote() and then in a xdmp:md5(). Now you can do a simple string comparison of the md5 of both results, and they should match. If they don't your test has failed.

Here's an example:

xquery version "1.0-ml";

import module namespace old = "ns:old-module" at "/folder/module.xqy";
import module namespace new = "ns:new-module" at "/folder/1.0-ml/module.xqy";

declare variable $item1 := old:function1("param 1");
declare variable $item2 := new:function1("param 1");

xdmp:md5(xdmp:quote($item1)) eq xdmp:md5(xdmp:quote($item2))
=> true

You can make a function of it and bang it in a test module that can be re-used in the rest of your tests . . .

xquery version "1.0-ml";

module namespace t = "ns:migration-test-suite";

declare function t:assertEquals($item1 as item()*, $item2 as item()) as xs:string
{if(xdmp:md5(xdmp:quote($item1)) eq xdmp:md5(xdmp:quote($item2))) then "passed" else "failed"};