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.
<?xmlversion="1.0"encoding="utf-8" ?>
<configuration>
<configSections>
<sectionname="specFlow"type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler, TechTalk.SpecFlow"/>
</configSections>
<specFlow>
<unitTestProvidername="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]
publicclassEmployeePromotionSteps
{
privateEmployee _employee = newEmployee();
privateEmployeeAppraisal _appraisal = newEmployeeAppraisal();
[Given(@"I have an employee called (.*)")]
publicvoid GivenIHaveAnEmployeeCalled(string name)
{
_employee.Name = name;
}
[Given(@"the employees current level is (.*)")]
publicvoid GivenTheEmployeesCurrentLevelIs(string level)
{
_employee.Level = level;
}
[Given(@"the employees current performance is (.*)")]
publicvoid GivenTheEmployeesCurrentPerformanceIs(int performance)
{
_employee.CurrentPerformance = performance;
}
[Then(@"the employee should not be recommended for promotion")]
publicvoid ThenTheEmployeeShouldNotBeRecommendedForPromotion()
{
MsTest.Assert.IsFalse(_appraisal.RecommendPromotion, "The employee should not be recommended for promotion");
}
[Then(@"the employees appraisal score should be (.*)")]
publicvoid ThenTheEmployeesAppraisalScoreShouldBe(int appraisalScore)
{
MsTest.Assert.IsTrue(_appraisal.AppraisalScore == appraisalScore, "The appraisal score is incorrect");
}
[Then(@"the employee should be recommended for promotion")]
publicvoid ThenTheEmployeeShouldBeRecommendedForPromotion()
{
MsTest.Assert.IsTrue(_appraisal.RecommendPromotion, "The employee should be recommended for promotion");
}
[When(@"The employees apraisal is evaluated by the promotion rules")]
publicvoid WhenTheEmployeesApraisalIsEvaluatedByThePromotionRules()
{
var policy = newPolicy("Specflow.EmployeeAppraisal");
var facts = newobject[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.