Brainstorming

4 views
Skip to first unread message

Jason Diamond

unread,
Aug 22, 2009, 6:03:33 PM8/22/09
to BehaveN
Cliff's idea about using anonymous methods got me thinking about ways
in which using BehaveN could be simplified.

Right now, I feel like setting things up to run scenarios written in
text is too different from scenarios written in code. For example,
scenarios written in text need the [Given], etc, attributes and they
need regular expressions to figure out the arguments.

I think the two approaches can be mostly homogenized.

I want to get rid of the attributes and regular expressions. I
realized that the patterns that Cliff and I worked out for
automatically formatting methods based on their names could be used to
match the methods, too.

For example, right now, you have to right this method to use from the
text syntax:

[When(@"adding (\d+) and (\d+)")]
public void adding_two_numbers(int a, int b)
{
_calculator.Add(a, b);
}

I want to change it to this:

public void When_adding_a_and_b(int a, int b)
{
_calculator,Add(a, b);
}

I can use reflection that uses the parameter types and names and the
method name to create the original regular expression.

Doing this brings the text syntax and the code syntax closer together.
No attributes, no regular expressions required in either case.

The other improvement I'd like to make is adding the steps to the
scenario in the code syntax.

Right now, we have to do this:

[Test]
public void AddingTwoNumbers()
{
Given(a_new_calculator);
When(adding_a_and_b, 1, 2);
Then(the_result_should_be, 3);
Verify();
}

Or using Cliff's suggestion:

[Test]
public void AddingTwoNumbers()
{
Add(Given_a_new_calculator);
Add(When_adding_a_and_b, 1, 2);
Add(Then_the_result_should_be, 3);
Verify();
}

What I'd really like is this:

public void AddingTwoNumbers()
{
Given_a_new_calculator();
When_adding_a_and_b(1, 2);
Then_the_result_should_be(3);
Verify();
}

There are a few advantages to this:

1. It looks more like the text syntax.
2. Any number of arguments are supported.
3. It looks more "natural" to me.

The only problem with this is that the the implementation of the
methods would have to change for it to work with the code syntax.

When_adding_a_and_b would have to look like this:

public void When_adding_a_and_b(int a, int b)
{
Execute(delegate { _calculator.Add(a, b); });
}

I need to "intercept" the actual code doing the work so that I can
infer the method name, catch any exception, filter the stack trace,
etc, so I continue executing the following steps.

It's a little ugly, but gets a tiny bit prettier if you're using .NET
3.5:

public void When_adding_a_and_b(int a, int b)
{
Execute(() => { _calculator.Add(a, b); });
}

OK, not that much prettier. Neither of these look natural to me.

The Execute method isn't necessary when using the text syntax, just
the code syntax. But it would work with both.

What do you think?

I have another idea using dynamic proxies, but it would require you to
put your scenario and step definitions in a separate class and marking
them as virtual. I'm not entirely sure if that would make it any
easier to use since you'd still have to write the test fixture that
invokes the methods in the other class. Of course, I could write my
own test runner (with a TD.NET plug-in, of course) or an add-in for
NUnit, but I was trying to avoid that.

--
Jason

Ranjit Thayyil

unread,
Aug 22, 2009, 11:10:48 PM8/22/09
to beh...@googlegroups.com
I assume that most of the developers moving towards behaveN would fall into the following two categories:

1. re-writing their tests to frame them in the behavioral context.
2. developers who have exposure to NUnit and learning TDD through BDD.

For both these categories, according to me, the easiest approach would be to add one method to either existing or new tests.


[TestFixture]
Public class ExistingTest
{
    [Test]
    public void CalcultorWorks()
    {
       Scenario scenario= new Scenario();
       
       scenario.Name("Calculator adds")
       .Given( a_calculator);
       .When( adding_the_numbers,1,2);
       .Then( result_should_be,3);
       .Verify();
    }
.
.
.
}

I prefer the above approach to :

public void AddingTwoNumbers()
{
   Given_a_new_calculator();
   When_adding_a_and_b(1, 2);
   Then_the_result_should_be(3);
   Verify();
}

and

[Test]
public void AddingTwoNumbers()
{
   Add(Given_a_new_calculator);
   Add(When_adding_a_and_b, 1, 2);
   Add(Then_the_result_should_be, 3);
   Verify();
}

because as a beginner, I can immediately tell in the first case that I am setting up a test with all the necessary GWT elements.
The approach with extensions can also provide some syntax help by wrapping the GWTS so that the extension methods available on a 'Given' are 'And' and 'When' and on a "When' is 'Then' etc

public static GivenWrapper Given(this NameWrapper extendedObject, object given)
        {

            DoWork();
            return new GivenWrapper();
        }

 public static GivenWrapper And(this GivenWrapper extendedObject,object given)
 {
            DoWork();
            return new GivenWrapper();
 }

public static WhenWrapper When(this GivenWrapper extendedObject, object when)
 {
            DoWork();
            return new WhenWrapper();
 }

public static WhenWrapper And(this WhenWrapper extendedObject,object when)
 {
            DoWork();
            return new WhenWrapper();
 }

public static ThenWrapper Then(this WhenWrapper extendedObject, object then)
 {
            DoWork();
            return new ThenWrapper();
 }

public static void Verify(this ThenWrapper extendedObject)
 {
            DoWork();
 }

I have not thought about other scenarios like parsing from a text file, so I am not sure if this would fit in that model.
 
--
Ranji

"This idea that there is generality in the specific is of far-reaching importance."
— Douglas Hofstadter, Gödel, Escher, Bach

Jason Diamond

unread,
Aug 26, 2009, 4:25:10 PM8/26/09
to beh...@googlegroups.com
Hi Ranjit,

Thanks for the reply. I hadn't thought of using BehaveN like that.

Even though I was really careful to make sure the Scenario class
supported method chaining, I completely forgot about that since I've
become used to deriving from Scenario.

With your wrappers, are you trying to say that you should only be able
to call Verify after a Then? That might be a good idea. Can we claim
that all scenarios will have at least one Given, one When, and one
Then? I'm OK with requiring that if it makes authoring your tests with
BehaveN easy and intuitive.

--
Jason

Ranjit Thayyil

unread,
Aug 26, 2009, 6:32:17 PM8/26/09
to beh...@googlegroups.com
The thought had skipped my mind that there maybe scenarios without Givens and Whens!
My thought was that the wrappers would restrict the order and give some syntactical guidance as well.

In case of missing GWTs, we could have empty Given().When().Then(...) ,  but that does not look very nice.

Jason Diamond

unread,
Aug 27, 2009, 7:19:56 PM8/27/09
to beh...@googlegroups.com
Or maybe something like GivenNothing()...

I can't imagine not having a When and Then, but in really simple
cases, I can see not requiring any setup, so no Given. We could just
allow either a Given or a When as the first step in the chain.

Also, I've seen us write tests that have multiple When/Then groups. So
after some Then steps, we go on and do some When steps and then more
Then steps. This is for complicated processes where we're trying to
verify the state of the system as it changes over time.

Basically, Then should allow more thens or a Verify, or going back to
another set of Whens.

Whether or not we actually constrain the API like this, I'm still
unsure about...

--
Jason

Reply all
Reply to author
Forward
0 new messages