Michael Stephenson

keeping your feet on premise while your heads in the cloud
posts - 358 , comments - 425 , trackbacks - 11

My Links

News

View Michael Stephenson's profile on BizTalk Blog Doc View Michael Stephenson's profile on LinkedIn

Twitter












Archives

Post Categories

Image Galleries

BizTalk

Mates

Friday, July 13, 2012

Developing BizTalk Maps with Specflow & BDD

Following on from the BDD articles I will cover how we have been using BDD development techniques to develop BizTalk maps. A BizTalk map is a discreet component in BizTalk which is ideally suited to development based on desired behaviours from the component. It fits well with our practices. If you imagine we are at a point where we have worked with our Business Analyst to work out the data formats required for various systems then we are in a position to work with them to begin understanding how the data transformation between systems needs to take place.

Using the Specflow feature file and gherkin syntax we have talked about previously gives us an excellent way to begin looking at the behaviour required from the map. In this scenario the maps behaviour is documented in the specflow example below. Note that this will be a very simple transformation.

Feature: Map System A Person to System B Person

    In order to load data into System B

    As a integration server

    I want to transform a person from the system A format to the system B format

 

Scenario: Default Scenario

    Given I have a person from System A

        | Firstname | Surname |

        | Joe | Bloggs |    

    When I execute the map

    Then the person in systems B full name should be JOE BLOGGS

 

Scenario: Person has no first name

    Given I have a person from System A

        | Firstname | Surname |

        | | Bloggs |    

    When I execute the map

    Then the person in systems B full name should be BLOGGS

 

Scenario: Person has no surname

        Given I have a person from System A

        | Firstname | Surname |

        | Joe | |    

    When I execute the map

    Then the person in systems B full name should be JOE With A Space

 

Scenario: Person has no name

        Given I have a person from System A

        | Firstname | Surname |

        | | |    

    When I execute the map

    Then the person in systems B full name should be empty

 

 

 

In this example you can see that I am using the table feature in Specflow to provide data from the specification which we will use behind the scenes in our test implementation to execute the test cases.

Building the Test

We now have a clear view of what the transformation needs to do so the Business Analyst can let us get on with developing our test implementation and then the map which we will develop again with the red/green/refactor process. The test implementation for this case will look something like the following.

In this test we will be using classes generated from the schemas and then populate the objects with data from the Specflow test. This gives us the excellent benefit of strong typing around the test data used with the map so that if the schema changes then we are able to catch compile errors and deal with this in a nice easy to handle way. Once we have the input class we will serialize it and use it to input into the map. We will then take the output from the map and deserialize it to the output object from where we will be able to use assertions against the object in the normal .net way.

A sample of the code behind this test is below:

using System.IO;

using System.Xml.Serialization;

using BizTalkStuff.Messages;

using Microsoft.BizTalk.TestTools.Mapper;

using Microsoft.BizTalk.TestTools.Schema;

using TechTalk.SpecFlow;

using Microsoft.VisualStudio.TestTools.UnitTesting;

 

namespace Test

{

[Binding]

public class StepDefinitions

{

private readonly SystemAPerson _systemAPerson = new SystemAPerson();

private SystemBPerson _systemBPerson = new SystemBPerson();

 

[Given(@"I have a person from System A")]

public void GivenIHaveAPersonFromSystemA(Table table)

{

var firstName = table.Rows[0]["Firstname"];

var surname = table.Rows[0]["Surname"];

 

_systemAPerson.Firstname = firstName;

_systemAPerson.Surname = surname;

}

 

[Then(@"the person in systems B full name should be (.*)")]

public void ThenThePersonInSystemsBFullNameShouldBe(string name)

{

if (name == "JOE With A Space")

name = "JOE ";

if(name == "empty")

name = string.Empty;

 

Assert.AreEqual(name, _systemBPerson.Fullname);

}

 

 

[When(@"I execute the map")]

public void WhenIExecuteTheMap()

{

const string testInputFileName = @"c:\maptestinput.xml";

const string outputFileName = @"c:\maptestoutput.xml";

 

SerializeInput(testInputFileName, _systemAPerson);

TestableMapBase map = new BizTalkStuff.SystemAPerson_To_SystemBPerson();

map.TestMap(testInputFileName, InputInstanceType.Xml, outputFileName, OutputInstanceType.XML);

_systemBPerson = DeserializeOutput<SystemBPerson>(outputFileName);

}

 

private static void SerializeInput(string filePath, object o)

{

using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write))

{

var serializer = new XmlSerializer(o.GetType());

serializer.Serialize(stream, o);

stream.Flush();

}

}

 

private static T DeserializeOutput<T>(string filePath)

{

using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))

{

var serializer = new XmlSerializer(typeof(T));

return (T)serializer.Deserialize(stream);

}

}

}

}

 

Building the Map

Before you build the tests you will have created your schema and generated the .net messages to help you write the tests, but you can now drive the TDD approach to building the map using the tests you have created. In this scenario the map was very simple and looked like the following:

 

 

Conclusion

Hopefully you can see that BizTalk maps are another good candidate where a BDD approach involving the use of Specflow can significantly reduce the total cost of ownership of the map because you have documentation of what the map is supposed to do in plain business like language. This was obviously a very simple sample but if you imagine some of the big maps you often come across where it's a complete nightmare to understand anything about them, this approach could really make life simpler for no additional effort.

The sample for this post is at the following location: https://s3.amazonaws.com/CSCBlogSamples/SpecFlowMapTest.zip

 

Posted On Friday, July 13, 2012 11:25 AM | Comments (0) | Filed Under [ BizTalk BizTalk Testing ]

BDD section on BizTalk Technet Wiki

Ive just added a new section to the BizTalk area of the technet wiki containing all of the various content about BDD and BizTalk

http://social.technet.microsoft.com/wiki/contents/articles/12322.behaviour-driven-development-with-biztalk.aspx

 

 

Posted On Friday, July 13, 2012 8:55 AM | Comments (0) | Filed Under [ BizTalk BizTalk Testing ]

Developing BizTalk Rules the BDD way

Recently we have been looking at changing the way we develop and test BizTalk Business Rules to use a BDD approach and our tool of choice to help with this is Specflow. I wanted to talk through how we do this and also why I think its useful.

I feel Business Rules are an excellent candidate for the BDD style approach to development that Specflow encourages.

It also promotes a TDD style where you can work with your BA to nicely document your rule requirements as you test and implement them. Some of the benefits offered by Specflow include:

  • The specflow test can act as scenario based documentation over how the rule is supposed to work
  • The documentation would be tested and therefore always accurate as part of your ALM process
  • The specflow test makes it much easier to maintain the rule

Ive blogged many times about the various benefits of specflow so lets look at a walk-through of this way of working.

The Specflow Feature File

First off I go into my Visual Studio solution and in an MsTest project and add the configuration to tell Specflow that I am using MsTest in Visual Studio 2010.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<configSections>

<section name="specFlow" type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler, TechTalk.SpecFlow"/>

</configSections>

<specFlow>

<unitTestProvider name="MsTest.2010" />

</specFlow>

</configuration>

 

Next I add a Specflow feature file.

Once I have my new feature I can begin working with my business analyst to document the desired rule behaviour. In the example below we are looking at a business rule which is used to evaluate an employees performance to decide if they should be awarded a promotion. The Gherkin for this looks something like the below:

Feature: Employee Promotion Rules

    In order to manage staff promotions

    As a hr manager

    I want to be able to calculate if an employee should be promoted or not

 

Scenario: Level 4 Employee should not be promoted regardless of score

    Given I have an employee called Joe

    And the employees current level is Level 4

    And the employees current performance is 100

    When The employees apraisal is evaluated by the promotion rules

    Then the employees appraisal score should be 100

    And the employee should not be recommended for promotion

 

Scenario: Level 1 Employee should not be promoted

    Given I have an employee called Joe

    And the employees current level is Level 1

    And the employees current performance is 25

    When The employees apraisal is evaluated by the promotion rules

    Then the employees appraisal score should be 25

    And the employee should not be recommended for promotion

 

Scenario: Level 1 Employee should promoted

    Given I have an employee called Joe

    And the employees current level is Level 1

    And the employees current performance is 26

    When The employees apraisal is evaluated by the promotion rules

    Then the employees appraisal score should be 26

    And the employee should be recommended for promotion

 

Scenario: Level 2 Employee should not be promoted

    Given I have an employee called Joe

    And the employees current level is Level 2

    And the employees current performance is 50

    When The employees apraisal is evaluated by the promotion rules

    Then the employees appraisal score should be 50

    And the employee should not be recommended for promotion

 

Scenario: Level 2 Employee should promoted

    Given I have an employee called Joe

    And the employees current level is Level 2

    And the employees current performance is 51

    When The employees apraisal is evaluated by the promotion rules

    Then the employees appraisal score should be 51

    And the employee should be recommended for promotion

 

Scenario: Level 3 Employee should not be promoted

    Given I have an employee called Joe

    And the employees current level is Level 3

    And the employees current performance is 75

    When The employees apraisal is evaluated by the promotion rules

    Then the employees appraisal score should be 75

    And the employee should not be recommended for promotion

 

Scenario: Level 3 Employee should promoted

    Given I have an employee called Joe

    And the employees current level is Level 3

    And the employees current performance is 76

    When The employees apraisal is evaluated by the promotion rules

    Then the employees appraisal score should be 76

    And the employee should be recommended for promotion

 

 

 

 

As you can see from the above it is very obvious what the expected behaviour should be from this rule which means I can be quite clear what I am supposed to develop. Also if you imagine that we completed the project and left it for 6 months then came back to it we could easily see what the rule is supposed to do.

Implementing the test behind the feature file

The next step is to implement the C# code which will be executed by the specflow test. In the code snippet below you can see the code which I have put into a C# file which the Specflow test will execute based on the given/when/then attribute usage. One of the things you may note is the use of wild cards in Specflow which make it easy to reuse the same step method to execute multiple gherkin statements. An example of this is in the GivenTheEmployeesCurrentLevelIs method where the wild card is used so that we can pass in the level from the gherkin in the feature file.

In the sample we have a simple employee and employee appraisal object which are populated with some data then ran through the business rule policy which will evaluate conditions and then amend the objects accordingly. After the policy is executed the test will make some assertions about how the objects should have been changed.

using TechTalk.SpecFlow;

using Acme.SpecFlow.BRETests.Rules.Facts;

using Microsoft.RuleEngine;

using MsTest = Microsoft.VisualStudio.TestTools.UnitTesting;

 

namespace Test

{

[Binding]

public class EmployeePromotionSteps

{

private Employee _employee = new Employee();

private EmployeeAppraisal _appraisal = new EmployeeAppraisal();

 

[Given(@"I have an employee called (.*)")]

public void GivenIHaveAnEmployeeCalled(string name)

{

_employee.Name = name;

}

 

[Given(@"the employees current level is (.*)")]

public void GivenTheEmployeesCurrentLevelIs(string level)

{

_employee.Level = level;

}

 

[Given(@"the employees current performance is (.*)")]

public void GivenTheEmployeesCurrentPerformanceIs(int performance)

{

_employee.CurrentPerformance = performance;

}

 

[Then(@"the employee should not be recommended for promotion")]

public void ThenTheEmployeeShouldNotBeRecommendedForPromotion()

{

MsTest.Assert.IsFalse(_appraisal.RecommendPromotion, "The employee should not be recommended for promotion");

}

 

[Then(@"the employees appraisal score should be (.*)")]

public void ThenTheEmployeesAppraisalScoreShouldBe(int appraisalScore)

{

MsTest.Assert.IsTrue(_appraisal.AppraisalScore == appraisalScore, "The appraisal score is incorrect");

}

 

[Then(@"the employee should be recommended for promotion")]

public void ThenTheEmployeeShouldBeRecommendedForPromotion()

{

MsTest.Assert.IsTrue(_appraisal.RecommendPromotion, "The employee should be recommended for promotion");

}

 

[When(@"The employees apraisal is evaluated by the promotion rules")]

public void WhenTheEmployeesApraisalIsEvaluatedByThePromotionRules()

{

var policy = new Policy("Specflow.EmployeeAppraisal");

var facts = new object[2];

facts[0] = _employee;

facts[1] = _appraisal;

policy.Execute(facts);

policy.Dispose();

}

}

}

 

 

This scenario is a very simple business rule to illustrate the technique but it should be possible to use the approach with any rule you want to develop. If you can test it with C# then you can test it with specflow.

Developing the Rule

At this point we have developed our specification for the behaviour of the rule and also the test cases which should prove the rule works and we can begin using red/green/refactor development techniques to build our rule. Lets start by developing the default rule for the default case.

Default Rule

In the default rule it's a bit like an else clause. Based on the specification of behaviour we will be focusing on the scenario where the employee is not level 2 or 3. We aim to prove that a level 4 employee would not be promoted regardless of their performance score.

The rule would look something like the below picture.

When we have added the implementation to the rule we should now be able to begin executing the test case for the level 4 employee and see that they are not promoted even if they score 100 in their performance. If we make any errors when implementing this rule we can work through these until the test goes green.

 

Adding the Level 1 Employee Rule

When developing the level 1 rule we are now focusing on the two test cases which test the level 1 employee. If we run these tests we will find that the not recommended for promotion test will pass straight away but the recommend for promotion test fails so we will add a new rule to the policy to check that a level 1 employee should be promoted if they score more than 25 points. The rule for this would look something like the below picture.

The two tests we will work with check that the employee is only promoted if they score 25 points or more. We should also rerun the test case for the default rule to make sure out level 4 employee is still not promoted.

 

Adding the Level 2 & 3 Rules

We would then continue to add the rules for the level 2 and 3 employees the same as we did for the level 1 employee ensuring we work through any errors as we add the rules and also check that the other tests still pass.

At the end of the implementation of the rules we may want to revisit the original specflow definition and to see if we need to add any additional scenarios to make sure we have adequately covered things like boundary conditions etc.

 

Conclusion

As you can see once our core cases are in place we are really just reusing the same specflow gherkin statements to add additional test cases and we can end up with a very solid set of test cases for our business rule ensuring that it is thourghly tested in isolation before we start trying to use the rule from within BizTalk which means that we should have no issues calling the rule from an orchestration as we know the rule executes exactly how we expect it too before we use it in our wider solution implementation.

Hopefully you can see combining Specflow with the traditional way of testing business rules from .net code adds a lot of additional value and lowers the cost of ownership of your code without requiring anymore effort.

 

The Sample

The sample is available at the following location: https://s3.amazonaws.com/CSCBlogSamples/SpecFlowBRETest.zip

In the sample you will find the xml file called BRERulePolicy.xml which contains the rule policy used in this example and you can then deploy that and play around with the tests. Please note that there is an msbuild snippet in the C# project which deploys the assembly to the GAC during compile so you may need to modify that to the location for gacutil on your machine.

 

Posted On Friday, July 13, 2012 7:41 AM | Comments (0) | Filed Under [ BizTalk BizTalk Testing ]

Powered by: