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

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 |

About Tom

Tom Janssens op LinkedIn

Tom Janssens op twitter

Core bvba RSS

 

 

Tom Janssens is an independent freelance ICT consultant that has been "into computers" ever since the age of 7.

Typing source code from a book evolved into exploring the limits of coding in procedural, assembly, object-oriented and functional languages.
As he matured in software coding, he started focusing on the problems surrounding software development, and learned that software development is usually about people and interactions first, and about technology second.

Due to his diverse track record he gained insights in a lot of aspects of the software development process. Currently his main focus is on strategic ICT advice, lean product/project development and improving the software development process and architecture.

He avoids ivory-tower-approaches by applying and verifying the applicability of the latest tech buzz in software experiments.

He is also the founder of the following LinkedIn groups:

CQRS Professional
BDD Professional
Asp.Net MVC professional

More info about Tom and his company...

**** Hire me ! ****

My current project will probably run till the end of August 2013. Feel free to contact me for a project later on.

I can work either in Belgium or via remote access!!!

You can find my resume here: resume.pdf .

Community contributions and publications: here .

Call me directly at +32 478 336 376


Advertisement

Forget all your SCRUM -, Kanban - and other Agile and Lean certificates

Here is the only true AGILE and LEAN certificate you will ever need:

The Creative Recursive Analysis Process Certificate
(CRAP Certificate for short)

More info can be found at the official CRAP certificate website:
http://bit.ly/CRAPCertificate