This project is read-only.

Exploring the Context

Introduction

BizUnit is a very extensible testing framework. One of the core elements of BizUnit is the Context object. Unfortunately the documentation does not explain how users can take advantage of it and it is left to developers to try and understand it from some of the code snippets provided in the NDoc documentation.

This tutorial uses examples from the official documentation wherever applicable and supplements it where the information in the documentation is not very clear. It will not replicate the list of methods and properties etc, so for an exhaustive reference, please refer back to the documentation. The three main context loader steps are explained here and future articles will explore custom steps as well as advanced uses of the context in unit and system testing.

Please note that some of the file paths refer to the structure of the BizUnit source code and the functional tests (for instance, the TestData folder is a specific folder in the set of unit tests that accompany the source) so I am assuming you have the code and can follow along.

While BizUnit is nice and easy to use, the issue with it is that for large test scripts, the BizUnit scripts can soon become unwieldy. For example, if you had a test case where data was picked up from a specified source file, loaded into a target, then validated and perhaps moved somewhere else, (maybe to an MSMQ queue or database), you may find that the filenames and folder names are duplicated across the test steps. Further, if you already have this data (folder names, filenames, connection strings etc) in a configuration file which you are using, say, for some NUnit tests that exercise non Biztalk artefacts), then it is quite unnecessary to hardcode all of them in the test xml file and as the test library grows it becomes very hard to maintain.

This is where the 'Context' object proves to be a great help.

The Context Object

The Context object represents a state object that is passed between BizUnit test steps. Inside the BizUnit system , the context object is passed to each individual test step. It cannot be initialised and used in the TestFixture class but is available for use in custom steps. In the Xml file corresponding to the test case, the context can be read and manipulated using the specified steps (prewritten or custom).

Inside the BizUnit system, state may be written to or read from the context. The context also provides helper methods for test steps to read their configuration, and to log information, warnings, errors and data in a consistent manner. When a test is executed, the detailed information available in the output window is produced by using the Context class. So if you are writing a custom step, you would be expected to log exceptions, errors and warnings etc using the methods provided in the Context class.

You can also use it to store information such as name-value pairs which help in the logic of the custom step. (Of course, this is not mandatory. Since the custom step is merely your own class, you can define and use your own private member variables as you wish).

As I mentioned in the Introduction section above, large scripts can soon become unmaintainable and so another important use for the context is to store configuration data that is used across test steps. For very simple test cases where we utilise only a couple of steps, this may not seem very useful, but as the test cases get bigger and when data gets duplicated, the storing of parameters in the context becomes crucial.

Using the Context : Basics

A simple way to think about Context is that it can be used like Nant properties. In NAnt we would declare a property variable with a specific name and store a value in it. Then in all the tasks you simply refer to the property using ${propertyname} and the system picks up the appropriate value.

In BizUnit, out of the box, we would first create a FileValidate step to point to our configuration data file. This step loads the file into memory (to avoid losing the file, set the <DeleteFile> to false). After this we can use the XmlContext Loader, TextContextLoader or RegExContextLoader to extract data from the source file.

The choice of context loader depends on the format of the data. The XmlContextLoader expects you to supply XPath expressions that allow it to navigate the source data and extract the values. The RegEx context loader expects to be supplied a regular expression to be applied to the source data (and strictly speaking, as long as the regular expression is correct it does not matter if the file is Xml or Text). The TextContextLoader is slightly different. It works like the Substring() method and the old VB6 Mid$() functions and looks for a specified pattern, index and string length to retrieve data from the file. (See below for a detailed example).

Which steps support context configuration data ?

Most of the steps (one notable exception being the DotNetObjectInvoker, and this has been corrected by introducting the DotNetObjectInvokerEx) support configuration data. This is not very clear in the documentation (although there are some hints here and there). In the Xml node (corresponding to any element in the step) , we need to use the attribute "takeFromCtx = <contextkeyname>" and the system will look up the context when it executes the step.

For example, consider the following FileCreateStep. Here the context has already been loaded using the appropriate context loader.

<TestStep assemblyPath="" typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.FileCreateStep">
<SourcePath takeFromCtx="sourcefilename"> </SourcePath>
<CreationPath takeFromCtx="targetfilename"> </CreationPath>
</TestStep>

In the SourcePath and CreationPath elements we use the takeFromCtx attribute and give it the context key name that the system should look up. The system then substitutes this at runtime.

Where it differs from Nant

A very important limitation of the context variables is that there is no run time 'macro expansion' behaviour beyond a simple context lookup.

For instance, in a Nant task, we can concatenate various properties together (along with system properties) so if we wanted a full file name, we could use something like

${source.path}\${file.name}.${file.extension}

The system would then substitute the property names with the appropriate values and use the supplied symbols such as the \ and the period (before the file extension) to arrive at the full file name. At this point in time this cannot be done in BizUnit, although it could be done by overloading the methods in the Context class to apply a macro expansion when the appropriate symbols such as $ and {} are detected.

There are a couple of macros that Kevin has introduced in BizUnit 2006 such as GUID, ServerName etc, but it would be better to have a way of referring to all system environment variables and maybe even user defined macros.

XmlContextLoader

Overview

The XmlContextLoader evaluates an XPath expression to the source data and adds the value into the context.

Example

The following shows an example of the Xml representation of this test step. In this example, the existence of file has been validated using the file validate step(not shown) and this file contains all the context variables.

The actual file used in this example is included along with the source code in the TestData folder. The file name is XmlContextData.xml. The contents are shown below

<Variables>
<sourcefilename>c:\temp\test.xml</sourcefilename>
<targetfilename>c:\temp\testtarget.xml</targetfilename>
<connectionstring>Persist Security Info=False;Integrated Security=SSPI;database=Northwind;server=(local);Connect Timeout=30</connectionstring>
<sourcefolder>c:\temp\</sourcefolder>
<targetfolder>c:\temp\</targetfolder>
</Variables>

Now when we use the ContextLoader we can load all the contents of the file into the context by using the correct type of context loader such as an XmlContext Loader or a RegEx context loader

Here we are using the XmlContext loader and the XPath expressions represent the navigation path to the data in the source file (i.e) 'Variables' is the top level element and 'sourcefilename' and 'targetfilename' are the elements which contain the values we want to load into the context Each of these XPath context key elements will result in the equivalent Context.Add(key,object) being executed.

<ContextLoaderStep assemblyPath="" typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.XmlContextLoader">
<XPath contextKey="sourcefilename">/local-name()'Variables'/local-name()'sourcefilename'</XPath>
<XPath contextKey="targetfilename">/local-name()'Variables'/local-name()'targetfilename'</XPath>
</ContextLoaderStep>

RegEx Context Loader

Overview

The RegExContextLoader applies a regular expression to the source data and adds the value into the context.

Example

The following shows an example of the Xml representation of this test step. In this example, the existence a file has been validated using the file validate step and this file contains all the context variables.

The actual file used in this example is included along with the source code in the TestData folder. The file name is RegExTestData.txt.

The content of the file is shown below

The BizTalk web site is here: http://www.microsoft.com/biztalk, you can find out more about the product there

Now when we use the ContextLoader we can load all the contents of the file into the context by using the correct type of context loader such as an XmlContext Loader or a RegEx context loader

Here we are using a RegEx context loader and adding in various items into the context. Each of these RegEx context key elements will result in the equivalent Context.Add(key,object) being executed. (For example the context key named HTTP_Url will now contain http://www.microsoft.com/biztalk.

<ContextLoaderStep assemblyPath="" typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.RegExContextLoader">
<RegEx contextKey="HTTP_Url">/def:html/def:body/def:p2/def:form</RegEx>
<RegEx contextKey="ActionID">/def:html/def:body/def:p2/def:form/def:input3</RegEx>
<RegEx contextKey="ActionType">/def:html/def:body/def:p2/def:form/def:input4</RegEx>
<RegEx contextKey="HoldEvent">/def:html/def:body/def:p2/def:form/def:input2</RegEx>
</ContextLoaderStep>

TextContextLoader

Overview

The TextContextLoader works like the Substring and the old VB6 Mid$ functions It searches through the source data for specified strings and then returns substrings from a specified position and for a specified length.

Example

The following shows an example of the Xml representation of this test step. In this example, the existence a file has been validated using the file validate step(not shown) and this file contains all the context variables. The actual file used in this example is included along with the source code in the TestData folder. The file name is TextContextLoaderDemo.txt. The file contains the following 2 lines

source=c:\temp\test.xml"
target=c:\temp\testtargetnew.xml"

In the following snippet we are using a TextContextLoader and adding in various items into the context.

<ContextLoaderStep assemblyPath="" typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.TextContextLoader">
<Item contextKey="sourcefilename" searchString="source" skipNumber="1" stringLength="16" />
<Item contextKey="targetfilename" searchString="target" skipNumber="1" stringLength="25" />
</ContextLoaderStep>

Now what happens is that when setting up the context key named "sourcefilename" , the system looks for the pattern/search string which is "source" and finds its IndexOf value. It then adds that to the skipNumber which is 1, thus making it ignore the "=" symbol in the line of data it then looks for the next 16 characters (which happens to be the full path name) and returns that. The same processing occurs for the next context key "targetfilename" and in this case it takes 25 characters.

This concept could be applied to a big string of parameters in the source file (if you dont want to bother with creating an Xml file and working out the necessary XPath expressions).

Limitation

This step does have one limitation in that the string lengths for the returned value need to be known in advance thus limiting the ability of the developer to easily change the test data. A future version of this step (or another custom step) can be created which allows a flexible loading of an array of context values.

ContextManipulatorStep

Overview

The ContextManipulator is used to manipulate BizUnit context fields. According to the documentation, it maybe used to create a new field from one or more existing fields.

Example

In the following example, a new context key named newsource is created and the value is taken from the existing context key.

<TestStep assemblyPath="" typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.ContextManipulatorStep">
<ContextItem contextKey="newsource">
<ItemTest takeFromCtx="sourcefilename"></ItemTest>
</ContextItem>
</TestStep>

Issue

Out of the box, this step does not appear to work no matter what attempt is made. It seems that the code is not written correctly for the step. (We would be better of writing our own step for this). I will be writing a custom step for this shortly and will post it at this site.

Summary

This article provided just a brief overview of how the context works and i hope this has given you the understanding you need to make use of it. In my current integration project we have written a few custom steps and made extensive use of the context to read our app.config and web.config files and it has proved invaluable in eliminating redundant declarations and hard-coding

Last edited Mar 24, 2007 at 9:09 PM by santoshbenjamin, version 2

Comments

No comments yet.