65.9K
CodeProject is changing. Read more.
Home

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

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0 vote)

Nov 11, 2009

CPOL
viewsIcon

6781

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:

    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:

    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