Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / All-Topics

Aubergine .NET BDD: Support for named/typed Parameters + RECURSIVE DSL + Bugfix

0.00/5 (No votes)
11 Nov 2009CPOL 6.8K  
Aubergine .NET BDD: Support for named/typed Parameters + RECURSIVE DSL + bugfix

Ok, I had some ideas this morning when I woke up, so I quickly implemented them.

Changes

Here is the change_log for the new version:

  • Bugfix given a DSL attribute without a parameter is called
  • DSL definition changed to named parameters/typeconverters

Example

This has simplified the more complicated DSL definitions a LOT; check out the new definition for the AccountContext:

C#
internal class AccountContext
{
    public Account AccountA = new Account();
    public Account AccountB = new Account();
    public Exception WhenException;

    [DSL(@"(?<account>Account[AB])_has_(?<amount>\d+)_m")]
    void accountX_has_Ym(Account account, decimal amount)
    {
        account.Balance = amount * 1m;
    }

    [DSL(@"it_should_have_(?<amount>\d+)_m_on_(?<account>Account[AB])")]
    void should_have_Xm_on_AccountY(Account account, decimal amount)
    {
        account.Balance.ShouldEqual(amount * 1m);
    }

    [DSL(@"transfering_(?<amount>\d+)_m_from_(?<from>Account[AB])_to_(?<to>Account[AB])")]
    void transfering_xm_from_a_to_b(decimal amount, Account from, Account to)
    {
        from.Transfer(amount * 1m, to);
    }

    [DSL(@"the_current_user_is_authenticated_for_(?<account>Account[AB])")]
    void authenticate_for_account_x(Account account)
    {
        account.IsAuthenticated = true;
    }

    [DSL]
    void it_should_fail_with_error()
    {
      (WhenException != null).ShouldEqual(true);
    }

    [DSL("(?<name>Account[AB])")]
    Account getaccountAB(string name)
    {
        return this.Get<Account>(name);
    }
}

Note the support for typed parameters and also DSL type converters. Due to the implication, the expressiveness has changed a lot: you can now do recursive dsl definitions !!! I'll get into this when I have more time, but really short: when you call a DSL function, the input string is pushed again to the interpreter. In theory, you could define a complete language like this !!!

In the example above, [DSL(@"the_current_user_is_authenticated_for_(?<account>Account[AB])")] is called, and the result for the group <account> is again pushed into the DSL engine; if a match is found, it is called, and the result of the function is returned; if not, it tries to do a Convert.ChangeyType(xxx,destintationtype);

Finally, for reference the Story as well as the output test results:

C#
class Transfer_money_between_accounts : Story<accountcontext>
{
    As_a user;
    I_want to_transfer_money_between_accounts;
    So_that I_can_have_real_use_for_my_money;

    Given AccountA_has_3_m;
    Given AccountB_has_2_m;

    [Cols("xx", "yy", "zz")]
    [Data(1, 2, 3)]
    [Data(2, 1, 4)]
    [Data(3, 0, 5)]
    class Transfer_xx_m_between_2_accounts : Scenario
    {
        Given the_current_user_is_authenticated_for_AccountA;
        When transfering_xx_m_from_AccountA_to_AccountB;
        Then it_should_have_yy_m_on_AccountA;
        Then it_should_have_zz_m_on_AccountB;
    }

    class Transfer_too_much : Scenario
    {
        Given the_current_user_is_authenticated_for_AccountA;
        When transfering_4_m_from_AccountA_to_AccountB;
        Then it_should_have_3_m_on_AccountA;
        Then it_should_have_2_m_on_AccountB;
        Then it_should_fail_with_error;
    }

    class Not_authorized_for_transfer : Scenario
    {
        When transfering_1_m_from_AccountB_to_AccountA;
        Then it_should_have_3_m_on_AccountA;
        Then it_should_have_2_m_on_AccountB;
        Then it_should_fail_with_error;
    }
}

Output

==STORY================================================================
   Transfer_money_between_accounts => OK
   Transfer_1_m_between_2_accounts => OK
      Given AccountA_has_3_m => OK
      Given AccountB_has_2_m => OK
      Given the_current_user_is_authenticated_for_AccountA => OK
      When transfering_1_m_from_AccountA_to_AccountB => OK
      Then it_should_have_2_m_on_AccountA => OK
      Then it_should_have_3_m_on_AccountB => OK
   Transfer_2_m_between_2_accounts => OK
      Given AccountA_has_3_m => OK
      Given AccountB_has_2_m => OK
      Given the_current_user_is_authenticated_for_AccountA => OK
      When transfering_2_m_from_AccountA_to_AccountB => OK
      Then it_should_have_1_m_on_AccountA => OK
      Then it_should_have_4_m_on_AccountB => OK
   Transfer_3_m_between_2_accounts => OK
      Given AccountA_has_3_m => OK
      Given AccountB_has_2_m => OK
      Given the_current_user_is_authenticated_for_AccountA => OK
      When transfering_3_m_from_AccountA_to_AccountB => OK
      Then it_should_have_0_m_on_AccountA => OK
      Then it_should_have_5_m_on_AccountB => OK
   Transfer_too_much => OK
      Given AccountA_has_3_m => OK
      Given AccountB_has_2_m => OK
      Given the_current_user_is_authenticated_for_AccountA => OK
      When transfering_4_m_from_AccountA_to_AccountB => OK
      Then it_should_have_3_m_on_AccountA => OK
      Then it_should_have_2_m_on_AccountB => OK
      Then it_should_fail_with_error => OK
   Not_authorized_for_transfer => OK
      Given AccountA_has_3_m => OK
      Given AccountB_has_2_m => OK
      When transfering_1_m_from_AccountB_to_AccountA => OK
      Then it_should_have_3_m_on_AccountA => OK
      Then it_should_have_2_m_on_AccountB => OK
      Then it_should_fail_with_error => OK

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)