Question about code structure

Mar 5, 2010 at 8:29 PM
Edited Mar 5, 2010 at 8:37 PM

Hi,

I've found StoryQ and I thing that you guys have done a really good job, thanks!

I was trying to rewrite a bunch of my current tests in the BDD style with StoryQ but my main problem was with the global variables that StoryQ force me to create in order to validate the Scenario. Usually I don't need those globals, in my tests they are local.

So, I'm wondering if while using StoryQ I should have a class by Scenario, in that case the globals make sense, but rather I prefer to have a method by Scenario, this match better my current tests implementation, but I end up with a bunch of globlals variables which should be locals. 

What do you guys suggest me!

--Erlis
PD: If the question is not clear, I can provide examples.

Coordinator
Mar 5, 2010 at 8:39 PM

Erlis,

I am trying to understand the problem fully. Are you wanting to do code like this thread? Or something else. Feel free to post some code and I can comment on what would do.

 

todd

Mar 5, 2010 at 8:54 PM
Edited Mar 5, 2010 at 9:03 PM

Hi todd,

Thanks for the promptly response. And yes, I'm trying to do code like that.

In my original TestFixture I have the following method:

 

[Test]
public void Should_read_a_defined_value() {
	// ~ ~ ~  S e T U p  ~ ~ ~ 
	NameValueCollection settings = new NameValueCollection();
	settings.Add( "TestKey", "TestValue" );
	ConfigManager configManager = new ConfigManager( settings );

	// ~ ~ ~  e X C e r c i S E  ~ ~ ~
	string result = configManager.Get( "TestKey" );

	// ~ ~ ~  t E S t  ~ ~ ~ 
	Assert.That( result, Is.EqualTo( "TestValue" ) );
}

As you can see I'm testing the Get method of the configManager class, which is local:

When Im trying to rewrite that test with StoryQ this is what happen:

...
private ConfigManager _configManager;
[SetUp] public void SetUpStory() { _story = new Story( "Creating a custom implementation of the configuration manager" ) .InOrderTo( "read values from the configuration file" ) .AsA( "user of the ConfigManager class" ) .IWant( "read defined values" ) .And( "reuse defined values expressed as tokens in other values" ); } ...
[Test] public void Read_a_defined_value() { // ~ ~ ~ S e T U p ~ ~ ~ NameValueCollection settings = new NameValueCollection(); settings.Add( "TestKey", "TestValue" ); ConfigManager configManager = new ConfigManager( settings ); // ~ ~ ~ Scenario _story.WithScenario( "Reading a defined value" ) .Given( AConfigManager_, configManager ) .When( IReadTheKey_, "TestKey" ) .Then( TheValueShouldBe_, "TestValue" ) .Execute(); } private void TheValueShouldBe_( string obj ) { throw new NotImplementedException(); } private void IReadTheKey_( string key ) { throw new NotImplementedException(); } private void AConfigManager_( ConfigManager configManager ) { _configManager = configManager; }

As you can see I was forced to declare a the ConfigManager class global. I also notice something wrong in my test, I'm not able to implement the Given/When/Then without using and OUT parameter.

I got the impression that I'm not doing this well.

Thanks for your help.
Erlis

Coordinator
Mar 5, 2010 at 9:42 PM
Edited Mar 5, 2010 at 9:42 PM

 

Erlis - there's three ways to pass around objects that you don't actually want printed as "parameters"

One, you can make them global variables like you have. This keeps your test classes quite focussed.

Two, you can use the [Silent] attribute on your parameters so that they aren't printed

Three, you can create some kind of context object, and call methods on that instead. StoryQ only looks at the method name, it doesn't care what object contains the method. The disadvantage here is readability (with the "t." breaking up the sentences):

 

        [Test]
        public void Read_a_defined_value()
        {
            TestUniverse t = new TestUniverse();

            new Story("Creating a custom implementation of the configuration manager")
                .InOrderTo("read values from the configuration file")
                .AsA("user of the ConfigManager class")
                .IWant("read defined values")
                .And("reuse defined values expressed as tokens in other values").WithScenario("Reading a defined value")
                    .Given(t.AConfigManager ,new NameValueCollection{ { "TestKey", "TestValue" } })
                    .When(t.IReadTheKey_, "TestKey")
                    .Then(t.TheValueShouldBe_, "TestValue")
                    .Execute();
        }

        class TestUniverse
        {
            public ConfigManager Manager { get; set; }
            public string Result { get; set; }

            public void AConfigManager([Silent] NameValueCollection settings)
            {
                Manager = new ConfigManager(settings);
            }

            public void IReadTheKey_(string obj)
            {
                Result = Manager.ReadKey(obj);
            }

            public void TheValueShouldBe_(string obj)
            {
                Assert.AreEqual(obj, Result);
            }
        }

 

 

Coordinator
Mar 5, 2010 at 9:51 PM
Edited Mar 5, 2010 at 10:02 PM

Erlis,

Here are some other thoughts without the test universe. ConfigManager is your system under test (SUT). You must have a copy of this available rather than hand it around. Hence your test code should remove this in the  Given. I think that what you have in the given is the key, value pair - the given is the setup data. The when/then are correct for the test.

In the implementation of when/then - you can see that they must both know about the SUT. Furthermore, the then must actually know about the key value of the when. It is this design that you see this type of test is not the same as you xunit test assertion. xunit assertions work directly on the SUT, storyq is a level of abstraction (and hence indirection). This causes more "wiring" code. This wiring code will require in fact an additional private variable containing the key! You could also create variables for the keys - particularly helpful if you start parametising the tests. Something like this.

private ConfigManager _configManager;
private string _key;

var _testkey = "TestKey";
var _testvalue = "TestValue";

	_story.WithScenario( "Reading a defined value" )
.Given( AConfigManager__, _testkey, _testvalue )
.When( IReadTheKey_, _testkey )
.Then( TheValueShouldBe_, _testvalue )
.Execute

private void IReadTheKey_( string key ) {
_key = _testkey; // rob's code read's here
}

private void TheValueShouldBe_( string val ) {

	Assert.That(val, Is.EqualTo(configManager.Get(_key)));  // I read here because conditions  may be set in the when/and

}

private void AConfigManager_( string key, string val) {

	_configManager = new ConfigManager{new NameValueCollection{{key,val}}};

}




Mar 6, 2010 at 2:58 AM
Edited Mar 6, 2010 at 3:00 AM

Hi toddb and robfe,

Thank a lot for your suggestions! This gave me a clear vision of my problem. It is true that in my case the ConfigManager class should be global, because as toddb said, is my SUT.

Particularly I liked the TestUniverse solution, because is a way to isolate the information needed for a specific scenario, this is like a response for my initial question: Do I need a class per specification?.

 What I don't like is to introduce some globals that are only relevant for a specific scenario. My original TestFixture is pretty simple, only two tests. But I have TestFixtures with much more tests. If I follow toddb's suggestion I'll end with some globals that indeed are part of the given of particular scenarios, this was the motivation of my first post. In a TestFixture with 5 tests for example, I could end with some globals that indeed should be locals of a specific scenario. On the other hand, with the Universe approach, I'll end up with an Universe class per scenario.

Guys, how do you organize your Stories? I was thinking that maybe instead of having a TextFixture that exercise a class(TDD mind), maybe it is better to have a package for the story, then classes per scenario(BDD mind). I just don't know how to do it well, an static story object available for the package?

Is there a better way to organize this? Have in mind that I would like to rewrite in the BDD way a bigger TextFixture, one simple method could generate multiple tests scenarios.

Thanks for your time, and for this wonderful tool
Erlis

Mar 25, 2010 at 10:58 AM
Hi guys, I have the same "issue". I specification = story = 1 TestFixture class. Test methods = scenarios. Using global variables is not really good for me except SUT because I can end up with bigger set of the variables. Using a TestUniverse is another solution but I need to write another class. I think the test universe solution is better than global variables ... but then to make the life easier it would be good to have a possibility to extend StoryQ converter to change the way how the code is generated i.e. using T4 templates. What do u think? Guys, anyway thanks for your time, and for this wonderful tool. We used NBehave but I was really happy to move to StoryQ. Frantisek
Coordinator
Mar 25, 2010 at 2:42 PM

Hi Frantisek

I thought about T4 too. But i'm not sure how easy it would be for the T4 developer to "import" the StoryQ data model. Another thought I had was to let you develop an XSLT against our own XSD, so you just had to put the XSD into your visual studio XSD directory to get intellisense on it. That's a thought anyway.

I read your blog (http://fknet.wordpress.com/2010/03/25/nbehave-out-storyq-in/) and I quite like the idea of a class being a story name and a method being a scenario name. I'm going to have to think about that some more!

-Rob