some tweaks

1 view
Skip to first unread message

powerdude

unread,
Aug 16, 2009, 11:52:05 AM8/16/09
to BehaveN
Hey Jason,

I've been playing around in the source and I made some tweaks to
implement some of the things i was looking for. i've uploaded the
changes in the files section. Let me know what you think.

cliff

powerdude

unread,
Aug 16, 2009, 11:56:32 AM8/16/09
to BehaveN
ok, it seems like i can't attach/upload a file, so here they are:

---------------- FormTests.cs ---------------------
[Test]
public void AutomaticConversionWithScenario()
{
Scenario scenario = new Scenario();
scenario.UseStepDefinitionsFrom(this);
_convertedObject = null;
scenario.VerifyText("Given a form\r\n" +
" :Foo: a\r\n" +
" :Bar: b\r\n" +
" :Baz: c\r\n");

Assert.That(_convertedObject, Is.Not.Null);
Assert.That(_convertedObject.Foo, Is.EqualTo("a"));
Assert.That(_convertedObject.Bar, Is.EqualTo("b"));
Assert.That(_convertedObject.Baz, Is.EqualTo("c"));
}

[Test]
public void AutomaticConversionWithScenarioDelegate()
{
var stringWriter = new StringWriter();
var form = new Form();

form.Add("Foo", "a");
form.Add("Bar", "b");
form.Add("Baz", "c");

var scenario = new Scenario
{
Reporter = new
PlainTextScenarioReporter(stringWriter)
};

scenario.Given(given_a_form, form);
scenario.Verify();

Debug.Print(stringWriter.ToString());
Assert.That(stringWriter.ToString().Trim(), Is.EqualTo
("Given a form\r\n" +
" :Foo: a\r\n" +
" :Bar: b\r\n" +
" :Baz: c"));

Assert.That(_convertedObject, Is.Not.Null);
Assert.That(_convertedObject.Foo, Is.EqualTo("a"));
Assert.That(_convertedObject.Bar, Is.EqualTo("b"));
Assert.That(_convertedObject.Baz, Is.EqualTo("c"));
}

[Test]
public void AutomaticConversionWithContextSpecification()
{
ContextSpecification cs = new ContextSpecification();
cs.UseStepDefinitionsFrom(this);
_convertedObject = null;
cs.VerifyText("Context: a form\r\n" +
" :Foo: a\r\n" +
" :Bar: b\r\n" +
" :Baz: c\r\n");

Assert.That(_convertedObject, Is.Not.Null);
Assert.That(_convertedObject.Foo, Is.EqualTo("a"));
Assert.That(_convertedObject.Bar, Is.EqualTo("b"));
Assert.That(_convertedObject.Baz, Is.EqualTo("c"));
}

private CustomObject _convertedObject;

[Given]
[Context]
public void a_form([Convert] CustomObject theObject)
{
_convertedObject = theObject;
}

public void given_a_form(Form theObject)
{
_convertedObject = theObject.ToObject<CustomObject>();
}

------------------------------------- Step.cs
--------------------------------------------------
internal class TextStep : Step
{
private readonly StepType _stepType;
private string _description;
private IConvertibleObject _convertibleObject;

public TextStep(StepType stepType, string description,
IConvertibleObject convertibleObject)
{
_stepType = stepType;
_description = description;
_convertibleObject = convertibleObject;
}

public TextStep(StepType stepType, string description)
:this(stepType,description,null)
{
}

protected string Description
{
get { return _description; }
set { _description = value; }
}

protected StepType StepType
{
get { return _stepType; }
}

protected IConvertibleObject ConvertibleObject
{
get { return _convertibleObject; }
set { _convertibleObject = value; }
}

public override void Execute(Specifications specifications)
{
specifications.ExecuteStep(_stepType,
_description,_convertibleObject);
}

public override void Skip(Specifications specifications)
{
specifications.ReportSkipped(_stepType, _description);

if (_convertibleObject != null)
{
specifications.ReportConvertibleObject
(_convertibleObject);
}
}
}

internal class DelegateStep : TextStep
{
private readonly Delegate _stepDelegate;
private readonly object[] _args;

public DelegateStep(StepType stepType, Delegate stepDelegate,
object[] args)
:base(stepType,"")
{
_stepDelegate = stepDelegate;
_args = args;

if (args != null && args.Length > 0 && args[0] is
IConvertibleObject)
ConvertibleObject = args[0] as IConvertibleObject;

Describe();
}

private void Describe()
{
Description =Regex.Replace
( _stepDelegate.Method.Name,"(when|then|given)
_?","",RegexOptions.IgnoreCase);

Description = Description.Contains("_")
? Description.Replace('_', ' ')
: string.Join(" ", Interpreter._splitter.Split
(Description)).ToLowerInvariant();

if (_args != null && _args.Length > 0)
{
if (_args[0] is IConvertibleObject)
{
Description += Environment.NewLine +
((IConvertibleObject)_args[0]).Format();
}
else
{
Description += " ";

bool first = true;

foreach (object arg in _args)
{
if (!first)
{
Description += " and ";
}

Description += arg;

first = false;
}
}
}
}

public override void Execute(Specifications specifications)
{
specifications.ExecuteStep(StepType, Description,
_stepDelegate, _args);
}
}

------------------------------- Interpreter.cs
----------------------------------------------
private static Regex GetRegexForMethod(string pattern,
MethodInfo method)
{
string description;

if (!string.IsNullOrEmpty(pattern))
{
description = pattern;
}
else
{
description = Regex.Replace(method.Name, "(when|then|
given)_?", "($1_)?", RegexOptions.IgnoreCase);

description = description.Contains("_") ?
description.Replace('_', ' ') : string.Join(" ", _splitter.Split
(description));
}

return new Regex("^" + description + "$",
RegexOptions.IgnoreCase);

Jason Diamond

unread,
Aug 16, 2009, 8:18:30 PM8/16/09
to beh...@googlegroups.com
Hi Cliff,

The easiest way for me to see the specific changes you made and
integrate them into my tree to run them would be if you sent me a
patch file.

If you're using the command line tool, from the root folder you
checked out to, enter this:

svn diff > my.patch

And then email me that my.patch file.

If you're using Tortoise SVN:

Right-click the folder, select Tortoise SVN, then select Create
Patch.... Select the files you changed, save the file, and email it.

--
Jason

Jason Diamond

unread,
Aug 17, 2009, 12:16:48 AM8/17/09
to beh...@googlegroups.com
Hi.

I just checked in the automatic formatting of methods like we talked
about. AddingAAndB(1, 2) now gets reported as "adding 1 and 2" if the
parameter names are a and b. I haven't done the Format attribute yet,
though.

Also, Scenario has a bunch of Add overloads for delegates now. Your
methods need to start with given/when/then or Add throws an exception.

Also, I used your code as an example and stripped away given/when/then
from method names and output the first convertible object found in the
arguments. Some more thought has to get put into how we can pass in
"real" objects and have them get reported, though.

It should be a lot closer to what you're hoping. Let me know if it
meets your needs. I want to make another release soon with all the new
goodies. =)

--
Jason

cliff vaughn

unread,
Aug 17, 2009, 11:45:13 AM8/17/09
to beh...@googlegroups.com
Jason,

On Mon, Aug 17, 2009 at 12:16 AM, Jason Diamond <jfdi...@gmail.com> wrote:

Hi.

I just checked in the automatic formatting of methods like we talked
about. AddingAAndB(1, 2) now gets reported as "adding 1 and 2" if the
parameter names are a and b. I haven't done the Format attribute yet,
though.

Great, can't wait to give this a go.

I found and issue with the previous request to modify the stack trace.  I think you may be also removing lines that are from the client code.  Usually, the last couple of lines are the test method that started the whole thing.  I haven't looked at the code to see how you're stripping off your stiff, but it might need some tweaking.

Also, i've been playing around with anonymous delegates.  See what you can do with this:

    [TestFixture]public class CalculatorTests
    {
        [Test]
        public void AddTwoNumbers()
        {
            var scenario = new CalculatorScenario();
            scenario.GivenANewCalculator().WhenAddingAAndB(1, 2).ThenTheResultIsResult1(3);
            scenario.Verify();
        }

        [Test]
        public void AddTwoNumbers2()
        {
            var scenario = new CalculatorScenario2();
            scenario.GivenANewCalculator().WhenAddingAAndB(1, 2).ThenTheResultIsResult1(3);
            scenario.Verify();
        }
    }

    internal class CalculatorScenario
    {
        private Scenario _scenario;
        private Calculator _target;

        public CalculatorScenario()
        {
            _scenario=new Scenario();
        }
        public void Verify()
        {
            _scenario.Verify();
        }

        public CalculatorScenario GivenANewCalculator()
        {
            _scenario.Given(() => _target = new Calculator());
            return this;
        }

        public CalculatorScenario WhenAddingAAndB(int a, int b)
        {
            _scenario.When((a1, b1) => _target.Add(a1, b1), a, b);
            return this;
        }

        public CalculatorScenario ThenTheResultIsResult1(int result1)
        {
            _scenario.Then(result => Assert.That(_target.Result, Is.EqualTo(result)),result1);
            return this;
        }
    }

    internal class CalculatorScenario2:Scenario
    {
        private Calculator _target;

       
        public CalculatorScenario2 GivenANewCalculator()
        {
            Given(() => _target = new Calculator());
            return this;
        }

        public CalculatorScenario2 WhenAddingAAndB(int a, int b)
        {
            When((a1, b1) => _target.Add(a1, b1), a, b);
            return this;
        }

        public CalculatorScenario2 ThenTheResultIsResult1(int result1)
        {
            Then(result => Assert.That(_target.Result, Is.EqualTo(result)), result1);
            return this;
        }
    }

    internal class Calculator
    {
        public void Add(int i, int b)
        {
            Result = i + b;
        }

        public int Result { get; set; }
    }


Also, Scenario has a bunch of Add overloads for delegates now. Your
methods need to start with given/when/then or Add throws an exception.

Also, I used your code as an example and stripped away given/when/then
from method names and output the first convertible object found in the
arguments. Some more thought has to get put into how we can pass in
"real" objects and have them get reported, though.

I think the way to handle this is to make the Form class smart enough, that when given an object, it just uses reflection to pull out all public, readable properties.  It could be made smart enough to travel down the hierarchy and output child objects too.  Of course this would/should be configurable in case a person's not interested in the rest of the hierarchy.  You should either be able to construct the Form with the object or Add the object to the form with some id.

 

It should be a lot closer to what you're hoping. Let me know if it
meets your needs. I want to make another release soon with all the new
goodies. =)

Finally, i noticed that scenarios can have names i think with context.  Any reason you don't output the name of the test method with the GWT stuff?
 
Keep up the good work!!!

cliff



--
thanks

cliff

cliff vaughn

unread,
Aug 17, 2009, 11:46:41 AM8/17/09
to beh...@googlegroups.com
I'll keep this in mind.  I'm using Ankh from VS, so i'll have to figure out a way to do that from VS first before I reinstall Tortoise.
--
thanks

cliff

Jason Diamond

unread,
Aug 17, 2009, 3:21:34 PM8/17/09
to beh...@googlegroups.com
Hi Cliff,

On Mon, Aug 17, 2009 at 8:45 AM, cliff vaughn<clifton...@gmail.com> wrote:
> I found and issue with the previous request to modify the stack trace.  I
> think you may be also removing lines that are from the client code.
> Usually, the last couple of lines are the test method that started the whole
> thing.  I haven't looked at the code to see how you're stripping off your
> stiff, but it might need some tweaking.

If you have an example, that would be cool. I'm using a regular
expression to only strip out lines that have the names of certain
BehaveN classes in them so I'd be surprised if I lost some real client
code. (Unless you're testing BehaveN...)

> Also, i've been playing around with anonymous delegates.  See what you can
> do with this:

That looks cool, but I'd have to think about it some more to try to
imagine if it would be worth it and implementable.

> I think the way to handle this is to make the Form class smart enough, that
> when given an object, it just uses reflection to pull out all public,
> readable properties.  It could be made smart enough to travel down the
> hierarchy and output child objects too.  Of course this would/should be
> configurable in case a person's not interested in the rest of the
> hierarchy.  You should either be able to construct the Form with the object
> or Add the object to the form with some id.

I was thinking about something like this, too, but then I noticed how
must of the forms and grids we have in our existing tests only display
subsets of the data from the real domain objects they're representing.
If it displayed too much, it would add too much noise to the report.

> Finally, i noticed that scenarios can have names i think with context.  Any
> reason you don't output the name of the test method with the GWT stuff?

Maybe you're thinking of the Name method. That's used to name the
scenario. In the plain text syntax, you use a line like this to name
it:

Scenario: My Scenario

I've been thinking about adding a way to infer the scenario name from
the test method name. I might have to use the API that lets you
examine the stack to figure that out. I think it can be done, I just
haven't gotten to it yet.

Thanks.

--
Jason

cliff vaughn

unread,
Aug 17, 2009, 3:38:41 PM8/17/09
to beh...@googlegroups.com
Hi Jason,

On Mon, Aug 17, 2009 at 3:21 PM, Jason Diamond <jfdi...@gmail.com> wrote:

If you have an example, that would be cool. I'm using a regular
expression to only strip out lines that have the names of certain
BehaveN classes in them so I'd be surprised if I lost some real client
code. (Unless you're testing BehaveN...)

I don't have an example because I don't have the stack trace available.  I just seem to remember before the change that the test method was at the bottom of the stack, but I think, now that i write this, that maybe it was when i called Verify() from the test method, but I lost it when i started calling Verify from a TearDown method.  Oh well.
 

> Also, i've been playing around with anonymous delegates.  See what you can
> do with this:

That looks cool, but I'd have to think about it some more to try to
imagine if it would be worth it and implementable.

that's fine.  My only request would be, and i don't know if it's there, but can you raise an event or provide an override where you pass the description and allow the user to return a new description.  then i could change it myself when i need to
 

> I think the way to handle this is to make the Form class smart enough, that
> when given an object, it just uses reflection to pull out all public,
> readable properties.  It could be made smart enough to travel down the
> hierarchy and output child objects too.  Of course this would/should be
> configurable in case a person's not interested in the rest of the
> hierarchy.  You should either be able to construct the Form with the object
> or Add the object to the form with some id.

I was thinking about something like this, too, but then I noticed how
must of the forms and grids we have in our existing tests only display
subsets of the data from the real domain objects they're representing.
If it displayed too much, it would add too much noise to the report.

I think you could maybe add a FilterBy( params string[] propertyNames) method, a constructor or something to allow the user to give a bunch of property names that the form could use to pull data out.  If the form has a list, use it, if it doesn't, get all properties.


> Finally, i noticed that scenarios can have names i think with context.  Any
> reason you don't output the name of the test method with the GWT stuff?

Maybe you're thinking of the Name method. That's used to name the
scenario. In the plain text syntax, you use a line like this to name
it:

Scenario: My Scenario

I've been thinking about adding a way to infer the scenario name from
the test method name. I might have to use the API that lets you
examine the stack to figure that out. I think it can be done, I just
haven't gotten to it yet.

Thanks.

yep, i think that's it.  i didn't really have a need for it, but it would support your idea of saving the output of the test run as possible input for others.


--
thanks

cliff
Reply all
Reply to author
Forward
0 new messages