Step 1: Create a Unit Test to Submit Documents to BizTalk Server

Computer application servers such as BizTalk Server are designed to perform particular tasks on behalf of users. These tasks are initiated as client requests sent to the application server as messages that conform to a standard that the application server understands, via a protocol that the application server understands. For example, clients may initiate processing of email by sending internet e-mail messages to an email server via the SMTP protocol. Likewise, web servers process client HTML or ASP requests, database servers process client SQL requests and BizTalk Server can process client messages formatted in compliance with multiple industry message standards using numerous industry standard protocols. The workload capacity of an application server is typically measured by the number of messages that the application server can process in a given time period. The workload capacity of BizTalk Server is likewise measured as the average number of “documents received per second”, “documents processed per second” and/or “orchestrations completed per second” over an extended period of time, such as a busy workday or even a work week. Visual Studio 2010 load test functionality can simulate a load profile of up to hundreds of users simultaneously accessing a server application. This load testing functionality provides real time metrics for selected key performance indicators as well as the ability to store these metrics in a database for future analysis. This document desribes the use of Visual Studio Test projects for the purpose of load testing a BizTalk Server application, including how to create unit tests, how to create load tests and how to configure load tests to capture performance counter data required to determine the Maximum Sustainable Throughput (MST) of a BizTalk Server application.

Creating a Visual Studio Unit Test to Submit Documents to BizTalk Server

A Visual Studio Unit test references the Microsoft.VisualStudio.TestTools.UnitTesting (https://go.microsoft.com/fwlink/?LinkID=132293) namespace which supplies several classes that provide support for unit testing. Of particular importance, the UnitTesting (https://go.microsoft.com/fwlink/?LinkID=132293) namespace includes the Microsoft.VisualStudio.TestTools.UnitTesting.TestContext (https://go.microsoft.com/fwlink/?LinkID=208233) class to store information provided to Unit tests and the Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute (https://go.microsoft.com/fwlink/?LinkID=208235) class is used to define Test methods. For purposes of load testing BizTalk Server, Test methods should specify the message to be loaded and the endpoint/URL to which the message should be sent. The endpoint/URL will then serve as the message entry point into BizTalk Server when a corresponding BizTalk receive location is created.

For purposes of illustration, the sample code in this topic describes Test methods which utilize the System.ServiceModel.ChannelFactory(TChannel) (https://go.microsoft.com/fwlink/?LinkID=208238) class to send messages to service endpoints that use the WCF net.tcp endpoint and are monitored by the BizTalk WCF-Custom receive adapter. Service endpoints for test messages are defined in the Application Configuration (app.config) file of the Test project.

For more information about Visual Studio Unit Tests see Anatomy of a Unit Test (https://go.microsoft.com/fwlink/?LinkID=208232).

Follow the steps in the sections below to create a Test project with a Unit Test to submit documents to one or more BizTalk Server computers. These steps were completed using Visual Studio 2010 Ultimate Edition.

Set Visual Studio 2010 Test Project Options

  1. Launch Visual Studio 2010 Ultimate edition. Click Start, point to All Programs, point to Microsoft Visual Studio 2010 and then click Microsoft Visual Studio 2010.

  2. In Visual Studio 2010, click Tools and then click Options to display the Options dialog box.

  3. Click to expand Test Tools and then click Test Project to display options for creation of new test projects.

  4. Set the Default test project language: to Visual C# test project.

  5. Under the option to Select the files that will be added to each new test project, by default: select Visual C# test project, and uncheck all of the test types for Visual C# test projects except for Unit Test.

  6. Click OK to close the Options dialog box.

Create a new Visual Studio 2010 Solution with a Test Project

  1. Create the folder C:\Projects on the Visual Studio 2010 Ultimate computer.

  2. In Visual Studio 2010 click File, point to New, and click Project to display the New Project dialog box.

  3. Under Installed Templates click to expand Visual C#, and click Test.

  4. At the bottom of the New Project dialog box specify the following options:

    • Name: BTSLoad

    • Location: C:\Projects\

    • Solution name: LoadTest

  5. Ensure that the option to Create directory for solution is checked and then click OK.

  6. Add a folder to the BTSLoad project; this folder will contain test messages to be submitted to BizTalk Server. In Solution Explorer, right-click the BTSLoad project, point to Add, and click New Folder. A folder icon with the highlighted text NewFolder1 will appear under the BTSLoad project, type TestMessages to change the highlighted text and press the Enter key to create the folder C:\Projects\LoadTest\BTSLoad\TestMessages.

Update the Code in the Test Project and add an Application Configuration File to the Test Project

  1. In Solution Explorer click to select UnitTest1.cs and replace the existing code with the following sample code listing:

    #region Using Directives
    using System;
    using System.IO;
    using System.Diagnostics;
    using System.Text;
    using System.Configuration;
    using System.Collections.Generic;
    using System.Linq;
    using System.Xml;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    #endregion
    
    namespace Microsoft.BizTalk.Samples
    {
        [TestClass]
        public class BTSLoadTest
        {
            #region Constants
            private const int MaxBufferSize = 2097152;
            private const string Source = "BTS Load Test";
            private const string Star = "*";
            private const string TestMessageFolderParameter = "testMessageFolder";
            private const string TestMessageFolderDefault = @"C:\Projects\LoadTest\BTSLoad\TestMessages";
            private const string TestMessageFolderFormat = @"Test Message Folder = {0}";
            private const string TestXmlDocument = "testxmldocument.xml";
            #endregion
    
            #region Private Instance Fields
            private TestContext testContextInstance;
            #endregion
    
            #region Private ThreadStatic Fields
            [ThreadStatic]
            private static ChannelFactory<IRequestChannel> channelFactory;
            [ThreadStatic]
            private static IRequestChannel channel = null;
            [ThreadStatic]
            private static byte[] buffer = null;
            #endregion
    
            #region Private Static Fields
            private static string testMessageFolder = null;
            #endregion
    
            #region Public Instance Constructor
            public BTSLoadTest()
            {
            }
            #endregion
    
            #region Public Static Constructor
            static BTSLoadTest()
            {
                try
                {
                    testMessageFolder = ConfigurationManager.AppSettings[TestMessageFolderParameter];
                    if (string.IsNullOrEmpty(testMessageFolder))
                    {
                        testMessageFolder = TestMessageFolderDefault;
                    }
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex.Message);
                    EventLog.WriteEntry(Source, ex.Message, EventLogEntryType.Error);
                }
            }
            #endregion
    
            #region Public Properties
            /// <summary>
            ///Gets or sets the test context which provides
            ///information about and functionality for the current test run.
            ///</summary>
            public TestContext TestContext
            {
                get
                {
                    return testContextInstance;
                }
                set
                {
                    testContextInstance = value;
                }
            }
            #endregion
    
            #region Test Methods
    
            [TestMethod]
            public void BTSMessaging()
            {
                InvokeBizTalkReceiveLocation("BTSMessagingEP",
                                               testMessageFolder,
                                               TestXmlDocument,
                                               MessageVersion.Default,
                                               SessionMode.Allowed);
            }
    
            [TestMethod]
            public void BTSMessaging2()
            {
                InvokeBizTalkReceiveLocation("BTSMessagingEP2",
                                               testMessageFolder,
                                               TestXmlDocument,
                                               MessageVersion.Default,
                                               SessionMode.Allowed);
            }
    
            [TestMethod]
            public void BTSOrchestration()
            {
                InvokeBizTalkReceiveLocation("BTSOrchestrationEP",
                                               testMessageFolder,
                                               TestXmlDocument,
                                               MessageVersion.Default,
                                               SessionMode.Allowed);
            }
            #endregion
    
            #region Helper Methods
            public void InvokeBizTalkReceiveLocation(string endpointConfigurationName,
                                               string requestMessageFolder,
                                               string requestMessageName,
                                               MessageVersion messageVersion,
                                               SessionMode sessionMode)
            {
                XmlTextReader xmlTextReader = null;
                Message requestMessage = null;
                Message responseMessage = null;
    
                try
                {
                    if (channel == null || channel.State != CommunicationState.Opened)
                    {
                        channelFactory = new ChannelFactory<IRequestChannel>(endpointConfigurationName);
                        channelFactory.Endpoint.Contract.SessionMode = sessionMode;
                        channel = channelFactory.CreateChannel();
                    }
                    if (buffer == null)
                    {
                        string path = Path.Combine(requestMessageFolder, requestMessageName);
    
                        string message;
                        using (StreamReader reader = new StreamReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)))
                        {
                            message = reader.ReadToEnd();
                        }
                        buffer = Encoding.UTF8.GetBytes(message);
                    }
                    MemoryStream stream = new MemoryStream(buffer);
                    xmlTextReader = new XmlTextReader(stream);
                    requestMessage = Message.CreateMessage(messageVersion, Star, xmlTextReader);
                    TestContext.BeginTimer(requestMessageName);
                    responseMessage = channel.Request(requestMessage);
                }
                catch (FaultException ex)
                {
                    HandleException(ex);
                    throw;
                }
                catch (CommunicationException ex)
                {
                    HandleException(ex);
                    throw;
                }
                catch (TimeoutException ex)
                {
                    HandleException(ex);
                    throw;
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                    throw;
                }
                finally
                {
                    TestContext.EndTimer(requestMessageName);
                    CloseObjects(xmlTextReader,
                                 requestMessage,
                                 responseMessage);
                }
            }
    
            private void HandleException(Exception ex)
            {
                try
                {
                    Trace.WriteLine(ex.Message);
                    EventLog.WriteEntry(Source, ex.Message, EventLogEntryType.Error);
                }
                catch (Exception)
                {
                }
            }
    
            private void CloseObjects(XmlTextReader xmlTextReader,
                               Message requestMessage,
                               Message responseMessage)
            {
                try
                {
                    if (xmlTextReader != null)
                    {
                        xmlTextReader.Close();
                    }
                    if (requestMessage != null)
                    {
                        requestMessage.Close();
                    }
                    if (responseMessage != null)
                    {
                        responseMessage.Close();
                    }
                }
                catch (Exception)
                {
                }
            }
    
            #endregion
        }
    }
    
  2. Add an Application Configuration file to the Test project:

    1. In Solution Explorer, right-click the BTSLoad project, point to Add and click New item.

    2. In the Add New Item dialog box, under Installed Templates, click General.

    3. In the list of items that are displayed click to select Application Configuration File and then click Add.

    4. In Solution Explorer select the app.config file and replace the contents of the app.config file with the sample code listing below:

      Important

      For each client endpoint defined in this file, BizTalk Server Computer is a placeholder for the actual name of the BizTalk Server computer(s) that you will perform load testing against.

      
      <configuration>
        <system.serviceModel>
          <!-- Bindings used by client endpoints -->
          <bindings>
            <netTcpBinding>
              <binding name="netTcpBinding" closeTimeout="01:10:00" openTimeout="01:10:00" receiveTimeout="01:10:00" sendTimeout="01:10:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="100" maxBufferPoolSize="1048576" maxBufferSize="10485760" maxConnections="400" maxReceivedMessageSize="10485760">
                <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
                <security mode="None">
                  <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
                  <message clientCredentialType="Windows" />
                </security>
              </binding>
            </netTcpBinding>
          </bindings>
          <client>
            <!-- Client endpoints used to exchange messages with WCF Receive Locations -->
      
            <!-- BTSMessagingEP -->
            <endpoint address="net.tcp://BizTalk Server Computer:8123/btsloadtest" binding="netTcpBinding" bindingConfiguration="netTcpBinding" contract="System.ServiceModel.Channels.IRequestChannel" name="BTSMessagingEP" />
            <endpoint address="net.tcp://BizTalk Server Computer:8123/btsloadtest" binding="netTcpBinding" bindingConfiguration="netTcpBinding" contract="System.ServiceModel.Channels.IRequestChannel" name="BTSMessagingEP" />
      
            <!-- BTSOrchestrationEP -->
            <endpoint address="net.tcp://BizTalk Server Computer:8122/btsloadtest" binding="netTcpBinding" bindingConfiguration="netTcpBinding" contract="System.ServiceModel.Channels.IRequestChannel" name="BTSOrchestrationEP" />
          </client>
        </system.serviceModel>
        <appSettings>
          <!-- Folder containing test messages -->
          <add key="testMessageFolder" value="C:\Projects\LoadTest\BTSLoad\TestMessages" />
          <add key="ClientSettingsProvider.ServiceUri" value="" />
        </appSettings>
      </configuration>
      
    5. Click the File menu in Visual Studio 2010 and then click Save All.

Add a Test Message to the Project

For purposes of this example, BizTalk Server receive location(s) and send port(s) will be configured to use pass through pipelines and will not perform any document validation. Follow these steps to add a test message to the project:

  1. Launch Notepad. Click Start, click Run and type Notepad in the Run dialog box.

  2. Copy the following text into Notepad and save as “C:\Projects\LoadTest\BTSLoad\TestMessages\TestXmlDocument.xml”

    <BTSLoadTest xmlns="http://Microsoft.BizTalk.Samples.BTSLoadTest">
    <MessageText>
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    This is sample message text. This is sample message text. This is sample message text. This is sample message text.
    </MessageText>
    </BTSLoadTest>
    
  3. Close Notepad.

Important

This file will need to be saved to the same path using the same file name on every Load Test Agent computer if multiple Load Test Agent computers are used for load testing.

Add Necessary References to the Project and Build the Test Project

  1. In Solution Explorer, right-click the References folder for the BTSLoad project and then click Add Reference.

  2. In the Add Reference dialog box, click the .NET tab and use the CTRL+Click keyboard/mouse combination to simultaneously select the following .NET namespaces:

    • System.Configuration

    • System.Runtime.Serialization

    • System.ServiceModel.Channels

    • System.ServiceModel

    • System.Web.Extensions

    • System.Xml

  3. After selecting the namespaces click OK to add these assemblies as references to the BTSLoad Test project.

  4. Right click the BTSLoad project and then click Build to compile the project into the BTSLoad assembly.