Aubergine .Net BDD : support for named/typed parameters + RECURSIVE DSL + bugfix + no more assertions

by Tom 7. November 2009 03:02

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


Changes (v0.02)

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

Be.Corebvba.Aubergine.Examples_v0.02.zip (16,62 kb)

 

Edit : v0.03

Still going; next changelog :

    * Test Status changed from bool to bool? => null = test implementation error
    * No more need for debug.assert; just use "bool It_should_have(amount x) { return amount==x;}

So : no more assertions (ASSERTIONS ARE EVIL >:) ), and if a test fails because there is an implementation error, the status will now return a null value instead of false.

Be.Corebvba.Aubergine.Examplesv0.03.zip (16,87 kb)

 

Edit : v0.04

look here

 

Example

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


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

        #region Given

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

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

        #endregion


        #region When

        [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);
        }
        #endregion

        #region Then

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

        [DSL]
        bool it_should_fail_with_error()
        {
           return WhenException != null;
        }

        #endregion

        #region Recursive DSL

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

        #endregion
    }

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 in to 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 &lt;account&gt; is agina 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

 

 

Bookmark and Share

Tags: , , , , ,

CodeProject | Development | News | Tom's blog

Comments

11/7/2009 4:37:00 AM #

trackback

BDD with DSL: "Aubergine", a ruby/cucumber like alternative for .NET -  download available

BDD with DSL: "Aubergine", a ruby/cucumber like alternative for .NET -  download available

Core bvba |

Comments are closed

About me

Tom Janssens op LinkedIn

Tom Janssens op twitter

Core bvba RSS

My name is Tom Janssens and I am the owner of Core bvba, a software and consultancy company.
I am married to Liesbeth and have 2 sons named Quinten and Matisse.
ICT is both my job and passion.
Next to this my other hobby is actively playing music (mostly guitar).

More info about me and my company...

Recent Comments