Geeks With Blogs

News


Dylan Smith ALM / Architecture / TFS

I've recently been involved with a project that involves a BizTalk 2006 application.  I've been focusing most of my time on improving the testing infrastructure, and trying to automate alot of the deployment and configuration tasks with the goal of getting to the point where we can setup a Continuous Integration environment.

I'm happy to say that I think we're pretty much there, but not without having to overcome some hurdles along the way.  This is my first encounter with BizTalk and it was a great learning experience.  What I realized early on is that BizTalk doesn't appear to be very tester-friendly.  I'm a TDD guy and I can see that trying to test-drive a BizTalk orchestration could prove tricky. It just seems like it behaves like too much of a black-box, and trying to develop in small increments and isolate specific functionality in the BizTalk components doesn't appear to be easily possible.  I find that testability comes from being able to have control over the component interactions, and the ability to monitor what is taking place.  When dealing with code projects this is typically achieved through loose-coupling, dependency injection, and/or mock objects.  With BizTalk your options are somewhat more limited.

I did manage to stumble onto a project called BizUnit which tries to tackle that problem.  I was brought into this project after the bulk of the BizTalk functionality was already implemented (although without any tests) so I haven't had the opportunity to try it out yet.  If anybody out there has experiences with it I'd love to hear what you think.  Or just how you tackle unit testing BizTalk in general.

Aside from being able to unit-test and TDD the BizTalk implementation, the other challenge has been trying to get a good testing infrastructure setup that is automated, fast, and easy to run.  The idea is that once we have everything setup, we should be able to setup a CI environment that can compile and run the test suite automatically on check-in's.

The main issue has been around the deployment of the BizTalk components.  In order to run any tests against the BizTalk projects they first need to be deployed into BizTalk (and the old versions undeployed).  Deployment is (or was) a manual process involving stopping the old application in BizTalk Admin, deleting the old application, deploying the new version from VS, importing the bindings in BizTalk Admin, and starting the application in BizTalk admin.  And this all had to take place any time any changes were made to the BizTalk projects.

My first thought was that surely all of this could be done from the command-line, all I need to do is throw together a few commands into a batch file or msbuild file to automate this process.  Unfortunately it's not that simple.  About the only thing from that process that can be done from the command-line is importing the bindings file.

Luckily I found the Enterprise Solutions Build Framework from Microsoft Consulting Services that promised to make life easier for me.  It is a set of MSBuild tasks that includes tasks for stopping and starting BizTalk applications, deleting and creating BizTalk applications, etc.  Exactly what I was looking for.Turns out the GotDotNet site hasn't been updated in over 6 months, and most of the links - including the downloads link - are broken.  I managed to find another site that has a sample project available for download that includes the SBF binaries.  It also includes a great sample of using the BizTalk tasks to uninstall and redeploy a BizTalk solution.  I modified the sample for my own use, the only really significant change was replacing his bindings stuff with an exec task that called out to BTSTask to import the bindings (the SBF includes a task to import the bindings, but neither the author of that sample nor myself could figure out how to get it to work).

Here is the MSBuild script that we are using to deploy our BizTalk components (client-identifying information replaced with *'s).  I have a bit of code that runs this in the ClassInitialize of my test fixture.

<Project  xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AppExists;TerminateInstancesRegistration;
          TerminateInstancesErrorHandler;StopApplication;UnDeploy;UnGacNetCode;UnGacBizTalk;
               ExeBuild;CreateApplication;DeployAssemblies;GacNetCode;ImportBindings;StartApplication;">
 <Import Project="..\tools\SBF\bin\bk\Microsoft.Sdc.Common.Tasks" />

 <PropertyGroup>
  <!-- Name of the solution - include the path relative to the build file-->
  <SolutionName>**************.sln</SolutionName>
  
  <BuildType>Release</BuildType>

  <!-- Path to the build dll files-->
  <BuildPath>BizTalkBuild\bin\</BuildPath>

  <!-- Name of the BizTalk Application to deploy code into -->
  <BTApplicationName>***********</BTApplicationName>

  <!-- Host for the Orch and Receive Location-->
  <HostName>BizTalkServerApplication</HostName>

  <!-- Set for a remote deployment -->
  <!-- Deploying BizTalk Server name - leave blank if local-->
  <BTServerName></BTServerName>
  <!-- Deploying BizTalk Server database - leave blank if BizTalkMsgBoxDb-->
  <BTServerDatabase></BTServerDatabase>
  <!-- Deploying BizTalk Server SQL user name - leave blank if local-->
  <BTServerUserName></BTServerUserName>
  <!-- Deploying BizTalk Server SQL password - leave blank if local-->
  <BTServerPassword></BTServerPassword>

  <!-- Internal -->
  <AppExists>False</AppExists>
 </PropertyGroup>

 <!-- List all .net items that need to be GACed -->
 <ItemGroup>
  <GacStuff Include="****************.Components" />
 </ItemGroup>

 <!-- List all BizTalk items in your project to deploy - must be in order of dependence -->
 <ItemGroup>
  <BTStuff Include="******************.Common" />
  <BTStuff Include="******************.Registration" />
  <BTStuff Include="******************.ErrorHandler" />
 </ItemGroup>

 <Target Name="AppExists" >
  <BizTalk2006.Application.Exists
   Application="$(BTApplicationName)"
   Server="$(BTServerName)"
   Database="$(BTServerDatabase)"
   Password="$(BTServerPassword)"
   UserName="$(BTServerUserName)">
   <Output TaskParameter="DoesExist" PropertyName="AppExists" />
  </BizTalk2006.Application.Exists>
  <Message text="App Exists: $(AppExists)" />
 </Target>

 <Target Name="TerminateInstancesRegistration" Condition="$(AppExists)=='True'" >
  <BizTalk2006.Orchestration.TerminateInstances
  AssemblyName="*********************.Registration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=761929dd8f17627e"
  Name="********************.ProcessRegistration.ProcessNewRegistration" />
  <Message text="Process New Registration Orch Killed" />
 </Target>

 <Target Name="TerminateInstancesErrorHandler" Condition="$(AppExists)=='True'" >
  <BizTalk2006.Orchestration.TerminateInstances
  AssemblyName="********************.ErrorHandler, Version=1.0.0.0, Culture=neutral, PublicKeyToken=761929dd8f17627e"
  Name="******************.BizTalk.ErrorHandler.ExceptionHandler" />
  <Message text="ErrorHandler Orch Killed" />
 </Target>

 <Target Name="StopApplication" Condition="$(AppExists)=='True'" >
  <BizTalk2006.Application.Stop
   Application="$(BTApplicationName)"
   Server="$(BTServerName)"
   Database="$(BTServerDatabase)"
   Password="$(BTServerPassword)"
   UserName="$(BTServerUserName)" />
  <Message text="App Stopped: $(BTApplicationName)" />
 </Target>

 <Target Name="UnDeploy" Condition="$(AppExists)=='True'" >
  <BizTalk2006.Application.Delete
   Application="$(BTApplicationName)"
   Server="$(BTServerName)"
   Database="$(BTServerDatabase)"
   Password="$(BTServerPassword)"
   UserName="$(BTServerUserName)">
  </BizTalk2006.Application.Delete>
  <Message text="App Deleted: $(BTApplicationName)" />
 </Target>

 <Target Name="UnGacNetCode" Condition="$(AppExists)=='True'">
   <GlobalAssemblyCache.RemoveAssembly
    AssemblyName="%(GacStuff.Identity)" />
   <Message text="Un Gaced: %(GacStuff.Identity)" />
 </Target>

 <Target Name="UnGacBizTalk" Condition="$(AppExists)=='True'">
  <GlobalAssemblyCache.RemoveAssembly
   AssemblyName="%(BTStuff.Identity)" />
  <Message text="Un Gaced: %(BTStuff.Identity)" />
 </Target>

 <Target Name="ExeBuild" >
  <Exec Command="&quot;C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\DevEnv&quot; $(SolutionName) /Build $(BuildType)" />
 </Target>

 <Target Name="CreateApplication" >
  <BizTalk2006.Application.Create
   Application="$(BTApplicationName)"
   Server="$(BTServerName)"
   Database="$(BTServerDatabase)"
   Password="$(BTServerPassword)"
   UserName="$(BTServerUserName)" />
  <Message text="App Created: $(BTApplicationName)" />
 </Target>

 <Target Name="DeployAssemblies" >
  <BizTalk2006.Assembly.Deploy
   Application="$(BTApplicationName)"
   AssemblyPath="$(BuildPath)%(BTStuff.Identity).dll"
   InstallInGac="true"
   Server="$(BTServerName)"
   Database="$(BTServerDatabase)"
   Password="$(BTServerPassword)"
   UserName="$(BTServerUserName)" />
  <Message text="Deployed: %(BTStuff.Identity)" />
 </Target>

 <Target Name="GacNetCode" >
  <GlobalAssemblyCache.AddAssembly
   AssemblyPath="$(BuildPath)%(GacStuff.Identity).dll" />
  <Message text="Gaced: %(GacStuff.Identity)" />
 </Target>

 <Target Name="ImportBindings" >
  <Exec Command="&quot;C:\Program Files\Microsoft BizTalk Server 2006\BTSTask.exe&quot; 
                 ImportBindings -Source:****************.BindingInfo.xml -ApplicationName:$(BTApplicationName)" />
 </Target>

 <Target Name="StartApplication" >
  <BizTalk2006.Application.Start
   Application="$(BTApplicationName)"
   Server="$(BTServerName)"
   Database="$(BTServerDatabase)"
   Password="$(BTServerPassword)"
   UserName="$(BTServerUserName)" />
  <Message text="App Started: $(BTApplicationName)" />
 </Target>
</Project>

 

Posted on Tuesday, April 10, 2007 10:36 PM | Back to top


Comments on this post: Continuous Integration with BizTalk

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Dylan Smith | Powered by: GeeksWithBlogs.net