Addressing some Behave# concerns


This post was originally published here.

So Joe and I have received some initial feedback for Behave#.  Joe’s already given a great intro into how to use Behave# and addressed Roy’s specific questions.  I thought I’d address some of the common issues regarding Behave#:

  • Using string matching
    • Using anonymous delegates
      • One developer supports it</ul>

        Using string matching

      One common concern I’ve heard from a couple of sources now is that Behave# uses strings to match behavior for subsequent scenarios.  Something that might not be clear on how we match behavior is that Scenarios are scoped to a Story.

      That is, the “context” parameter of a “Given” scenario fragment is only able to be matched against other Scenarios within a single Story.  Here’s an example:

      [Test]
      public void Withdraw_from_savings_account()
      {
      
      

    Account savings = null; Account cash = null;

    Story transferStory = new Story(“Transfer to cash account”);

    transferStory .AsA(“savings account holder”) .IWant(“to transfer money from my savings account”) .SoThat(“I can get cash easily from an ATM”);

    transferStory .WithScenario(“Savings account is in credit”) .Given(“my savings account balance is”, 100, delegate(int accountBalance) { savings = new Account(accountBalance); }) .And(“my cash account balance is”, 10, delegate(int accountBalance) { cash = new Account(accountBalance); }) .When(“I transfer to cash account”, 20, delegate(int transferAmount) { savings.TransferTo(cash, transferAmount); }) .Then(“my savings account balance should be”, 80, delegate(int expectedBalance) { Assert.AreEqual(expectedBalance, savings.Balance); }) .And(“my cash account balance should be”, 30, delegate(int expectedBalance) { Assert.AreEqual(expectedBalance, cash.Balance); })

    .Given(<span class="str">"my savings account balance is"</span>, 400)
    .And(<span class="str">"my cash account balance is"</span>, 100)
    .When(<span class="str">"I transfer to cash account"</span>, 100)
    .Then(<span class="str">"my savings account balance should be"</span>, 300)
    .And(<span class="str">"my cash account balance should be"</span>, 200);
    

    Story withdrawStory = new Story(“Withdraw from savings account”);

    withdrawStory .AsA(“savings account holder”) .IWant(“to withdraw money from my savings account”) .SoThat(“I can pay my bills”);

    withdrawStory .WithScenario(“Savings account is in credit”) .Given(“my savings account balance is”, 100); // This entry doesn’t have a match!

} </pre> </div>

    In the &#8220;withdrawStory&#8221;, even though the &#8220;Given&#8221; fragment string matches the &#8220;transferStory&#8221; &#8220;Given&#8221; fragments, the behavior will not match up.&nbsp; That&#8217;s because Scenarios belong to a Story, and the matching only happens within a given Story.
    
    In DDD terms, the Aggregate Root is the Story, and the child Entities include the Scenarios.&nbsp; We _could_ match across stories, but that wouldn&#8217;t adhere to DDD guidelines, and would result in much more complexity.
    
    So matching issues only happen within one Story.&nbsp; I don&#8217;t know how many Scenarios you would need to write before running into issues, but I think we could follow some of Joe&#8217;s suggestions and use some more intelligent matching algorithms.
    
    #### Using anonymous delegates
    
    Let me be the first to admit that anonymous delegates are clunky, difficult, and just plain ugly.&nbsp; But keep in mind that Behave# only deals with delegates.&nbsp; How the consuming test code creates these delegates does not matter to Behave#.&nbsp; We have several options (asterisk next to C# 3.0 features):
    
      * [Named methods](http://msdn2.microsoft.com/en-us/library/98dc08ac(VS.80).aspx) 
          * [Anonymous methods](http://msdn2.microsoft.com/en-us/library/0yw3tz5k(VS.80).aspx) 
              * [Lambda expressions](http://msdn2.microsoft.com/en-us/library/bb397687(VS.90).aspx)*</ul> 
            Anonymous methods may not be very prevalent in C# 2.0, but there are still quite a few classes in the DNF that use delegate arguments for method parameters.&nbsp; I ran the following code against .NET 3.5 assemblies:
            
            <div class="CodeFormatContainer">
              <pre>var types = from name <span class="kwrd">in</span> assemblyNames
        select Assembly.LoadWithPartialName(name) into a
        from c <span class="kwrd">in</span> a.GetTypes()
        <span class="kwrd">where</span> (c.IsClass || c.IsInterface) && c.IsPublic && !c.IsSubclassOf(<span class="kwrd">typeof</span>(Delegate))
        select c.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) into methods
        from method <span class="kwrd">in</span> methods
        <span class="kwrd">where</span> method.GetParameters().Any(pi =&gt; pi.ParameterType.IsSubclassOf(<span class="kwrd">typeof</span>(Delegate)))
            && !method.Name.StartsWith(<span class="str">"add_"</span>, StringComparison.OrdinalIgnoreCase)
            && !method.Name.StartsWith(<span class="str">"remove_"</span>, StringComparison.OrdinalIgnoreCase)
        select <span class="kwrd">new</span> { TypeName = method.DeclaringType.FullName, MethodName = method.Name };

int methodCount = types.Count(); int typeCount = types.GroupBy(t => t.TypeName).Count();

Debug.WriteLine(“Method count: “ + methodCount.ToString()); Debug.WriteLine(“Type count: “ + typeCount.ToString()); </pre> </div>

            And I found that there are 1019 methods with delegate parameters spread out over 155 types.&nbsp; With the System.Linq.Enumerable extension methods in .NET 3.5, methods with delegate parameters will be used much more often.
            
            #### Only one developer supports it
            
            Well&#8230;not exactly true.&nbsp; There are two developers, Joe and I, so that&#8217;s a 100% improvement, right? 🙂
            
            #### Wrapping it up
            
            I really like [Dan North&#8217;s](http://dannorth.net/) [rbehave](http://dannorth.net/2007/06/introducing-rbehave).&nbsp; Behave# closely matches rbehave&#8217;s usage and intent.&nbsp; Are a lot of the same issues regarding Behave# also valid for rbehave?&nbsp; Ruby lends itself well to BDD, especially when combining rbehave and [rspec](http://rspec.rubyforge.org/), with the elegance of dynamic typing and language features like blocks.
            
            Behave# is still getting started, and we have some kinks to iron out, but I do think we&#8217;re on the right track, following in Dan North&#8217;s footsteps.
Some Behave# news