Testing Pipeline Components
As many of you know, there are basically two ways to test custom Pipelines and custom Pipeline Components in BizTalk Server:
- To create them and test them by using them "for real" in biztalk messaging scenarios: i.e. configure ports to use your custom pipelines and feed messages through them.
- Use the pipeline.exe tool included in the BizTalk SDK to run them standalone.
Option 1 is required, definitely a needed option, but it is inconvenient, at the least, for agile development: it is slow, cumbersome, and hard to automate. Option 2 is easier and faster, but it's also inconvinient to automate.
So I had been looking at ways to automate the testing of pipelines and pipeline components, and ran across this article, explaining how to use mocks to unit test pipeline components. Neat, but inconvenient as well. Furthermore, it does not allow you to test pipeline components inside a real pipeline, which is sometimes necessary to ensure your component works well with the built-in components in BizTalk (disassembler in particularly can be pretty picky).
I realized then that MS already provided most of what you needed to test pipeline components in a better way, and one which was possible to embed inside NUnit tests (or whatever unit testing framework you use): Pipeline.exe relies on a set of helper components in the PipelineObjects.dll assembly included with the SDK, which does the heavy work of mocking the most important BizTalk objects that execute pipelines, such as IPipelineContext, IBaseMessage and IBaseMessageFactory and so on. I figured that, with a nicer API built on top of this, I could come up with something that really made it easier to test your pipeline components in a more agile manner, and that complemented the core unit tests you are creating for internal functionality.
So I've been working on this for the past couple of days and I've already have a working implementation. Here's an example of what it might look like:
///
<summary>
///
Tests that we can execute successfully a loaded pipeline
///
with a flat file as input
///
</summary>
[Test]
public
void Test_ExecuteOK_FF()
{
ReceivePipelineWrapper pipeline =
PipelineFactory.CreateReceivePipeline(typeof(CSV_FF_RecvPipeline));
//
Create the input message to pass through the
pipeline
Stream stream =
DocLoader.LoadStream("CSV_FF_RecvInput.txt");
IBaseMessage inputMessage = MessageHelper.CreateFromStream(stream);
inputMessage.BodyPart.Charset
= "UTF-8";
// Add
the necessary schemas to the pipeline, so that
//
disassembling works
pipeline.AddDocSpec(typeof(Schema3_FF));
//
Execute the pipeline, and check the output
MessageCollection outputMessages = pipeline.Execute(inputMessage);
Assert.IsNotNull(outputMessages);
Assert.IsTrue(outputMessages.Count > 0);
}
The code above does the following:
- Loads a receive pipeline, which in this case contains a Flat File Disassembler
- Creates a new input message with a Flat File loaded from a resource stream
- Loads the Flat File Schema the FFDasm will use to parse the message and makes it available to the pipeline
- Execute the pipeline and checks the output.
It's cetainly not a lot of code, and personally I think the API is looking pretty nice right now. So far, I got it working with the following scenarios:
- Support for both receive and send pipelines
- You can create pipelines programatically, by adding components to any stage, or load an existing biztalk pipeline (the preffered method) using its type.
- You can make schemas known to the pipeline before executing it by loading the necessary biztalk schemas out of a biztalk assembly. I've tested it already with both XML and Flat File schemas, schemas with one or multiple roots, schemas with promoted properties, and envelope schemas.
- Both debatching receive pipelines and batching send pipelines are supported through the API.
Here's another example, this time of doing multiple document batching into an envelope using the XML Assembler:
///
<summary>
///
Tests we can execute a send pipeline with
///
multiple input messages and an envelope
///
</summary>
[Test]
public
void
Test_ExecuteOK_MultiInput()
{
SendPipelineWrapper pipeline =
PipelineFactory.CreateSendPipeline(typeof(Env_SendPipeline));
//
Create the input message to pass through the
pipeline
string body =
@"<o:Body
xmlns:o='http://SampleSchemas.SimpleBody'>
this is a body</o:Body>";
MessageCollection inputMessages = new MessageCollection();
inputMessages.Add(MessageHelper.CreateFromString(body));
inputMessages.Add(MessageHelper.CreateFromString(body));
inputMessages.Add(MessageHelper.CreateFromString(body));
// Add
the necessary schemas to the pipeline, so that
//
assembling works
pipeline.AddDocSpec(typeof(SimpleBody));
pipeline.AddDocSpec(typeof(SimpleEnv));
//
Execute the pipeline, and check the output
// we
get a single message batched with all the
//
messages grouped into the envelope's body
IBaseMessage outputMessage = pipeline.Execute(inputMessages);
Assert.IsNotNull(outputMessage);
using ( StreamReader
reader = new
StreamReader(outputMessage.BodyPart.Data)
)
{
// d contains the entire output
message
string d =
reader.ReadToEnd();
}
}
One important difference with working with the API I'm creating and pipeline.exe is that the API is meant to be used with existing biztalk artifacts, which you can create with regular biztalk projects (though they don't need to be deployed to use them). Pipeline.exe, on the other hand, works with the raw *.btp and *.xsd files, which is simpler for some things, but far more inconvinient for testing, at least in my opinion.
I should be ready to post this in a few days, as I still need to test it more throughly and fix a few things, but if anyone is interested, let me know and I'll pass on what code I have right now. Do note I'm implementing this for BTS06 only, though I see no reason why you couldn't port it to BTS04!





