4
\$\begingroup\$

For a little background, I'm a contributor to SquishIt and decided that I should start cleaning up the mess of unit tests (my updated version of one set of tests vs. original set of tests). After tackling only part of the "rewrite", I figured it would be a good opportunity to learn, try, and understand BDD style testing to hopefully help improve the unit tests being written and ran. I decided to go with NSpec after taking a look at some of the choices out there.

My problem is simply trying to break the tests down into the appropriate "groupings" that logically make sense and "conform" to xSpec. Here is what I've done so far and passes all tests as expected:

using NSpec;
using SquishIt.Framework;
using SquishIt.Framework.JavaScript;

namespace SquishIt.Tests
{
    class describe_JavaScriptBundle : nspec
    {
        public JavaScriptBundle bundle;

        public void AddTest1JS()
        {
            bundle.Add("test.js");
        }
    }

    class when_I_create_a_bundle : describe_JavaScriptBundle
    {
        void before_each()
        {
            if (bundle == null)
            {
                bundle = Bundle.JavaScript();
            }
        }

        void and_add_no_files()
        {
            it["GroupBundles containskey default"] = () => bundle.GroupBundles.ContainsKey("default").is_true();
            it["has no Assets"] = () => bundle.GroupBundles["default"].Assets.Count.Is(0);
        }
    }

    class then_I_add_a_file : when_I_create_a_bundle
    {
        void before_each()
        {
            AddTest1JS();
        }

        void it_should_contain_only_one_file()
        {
            bundle.GroupBundles["default"].Assets.Count.Is(1);
        }
    }

    class then_I_add_the_same_file : then_I_add_a_file
    {
        void it_should_still_contain_only_one_file()
        {
            bundle.GroupBundles["default"].Assets.Count.Is(1);
        }
    }
}

How can I improve this or am I already on the right track?

\$\endgroup\$
  • 1
    \$\begingroup\$ I usually shy away from inheritance until I have significant nesting inside of my method contexts. Your tests communicate intent and test behavior. That's really all that matters in a good test suite :-) \$\endgroup\$ – Amir Aug 8 '11 at 18:49
4
\$\begingroup\$

Here is how I've written most of my NSpec tests, I usually start a context out with an act. This dry's up the specification. If I find myself doing too many "acts", I end up breaking it up into separate specifications.

class describe_JavaScriptBundle : nspec
{
    JavaScriptBundle bundle;

    void AddTest1JS()
    {
        bundle.Add("test.js");
    }

    void before_each()
    {
        bundle = Bundle.JavaScript();
    }

    void adding_a_bundle()
    {
        context["no files added"] = () =>
        {
            it["GroupBundle contains default key"] = () =>
                bundle.GroupBundles.ContainsKey("default").is_true();

            it["has no Assets"] () => 
                bundle.GroupBundles["default"].Assets.Count.Is(0);
        };

        context["adding one file"] = () =>
        {
            act = () => AddTest1JS();

            it["contains the file"] = () => 
                bundle.GroupBundles["default"].Assets.Count.Is(1);

            context["adding the same file again"] = () =>
            {
                act = () => AddTest1JS();

                it["should still only contain one file"] = () => 
                    bundle.GroupBundles["default"].Assets.Count.Is(1);
            };
        };
    }
}
\$\endgroup\$
1
\$\begingroup\$

I've just started out using NSpec myself last night. I really like how expressive it can be, though I see how easy it is to get into a bit of a class inheritance mess (I certainly did last night! :).

Here's my suggested way of coding your test:

public class describe_JavaScriptBundle : nspec
{
    protected JavaScriptBundle bundle;

    private void CreateBundle()
    {
        if (bundle == null)
        {
            bundle = Bundle.JavaScript();
        }
    }

    private void after_each()
    {
        bundle = null;
    }

    public void Bundle_Contains_No_Files()
    {
        it["When I create a new bundle"] = this.CreateBundle;
        it["And bundle contains no files"] = () => { };
        it["Then bundle will contain default key"] = () => { };
        it["And bundle will have no assets"] = () => { };
    }

    public void Bundle_Contains_One_File()
    {
        it["When I create a new bundle"] = this.CreateBundle;
        it["And bundle contains one file"] = () => bundle.Add("test.js");
        it["Then bundle should have one asset"] = () => { };
    }

    public void Add_Same_File_To_Bundle()
    {
        it["When I create a new bundle"] = this.CreateBundle;
        it["And bundle contains one file"] = () => bundle.Add("test.js");
        it["When I add the same file to the bundle"] = () => bundle.Add("test.js");
        it["Then bundle should have one asset"] = () => { };
    }
}

The key here is the output you get:

describe JavaScriptBundle <- Feature
  Bundle Contains No Files <- Scenario
    When I create a new bundle
    And bundle contains no files
    Then bundle will contain default key
    And bundle will have no assets
  Bundle Contains One File <- Scenario
    When I create a new bundle
    And bundle contains one file
    Then bundle should have one asset
  Add Same File To Bundle <- Scenario
    When I create a new bundle
    And bundle contains one file
    When I add the same file to the bundle
    Then bundle should have one asset

Notice how I try to describe the feature and then each scenario. This way I'm able to visually see how each scenario it tested. By no means am I saying that having inheritance in wrong, it's just in your case I don't think the tests are complicated enough to warrant this.

So fill in the blanks for the tests and give it a whirl. Let me know if it works out for you.

Cheers. Jas.

\$\endgroup\$
  • \$\begingroup\$ NSpec is geared toward Context/Specification. While you can do Gherkin style testing in NSpec, it's better suited for nesting context. I've added an example that shows how you can nest context, hopefully it doesn't hurt the readability of the specification. \$\endgroup\$ – Amir Aug 8 '11 at 18:44
  • 1
    \$\begingroup\$ On a side note, it's worth noting that NSpec doesn't guarantee that you'll get the same instance of the NSpec test across "it" blocks. \$\endgroup\$ – Amir Aug 8 '11 at 18:55
  • \$\begingroup\$ Thanks for the tips. In your second comment, is there documentation which explains this further? What is meant by "....same instance of the NSpec test..."? Cheers. \$\endgroup\$ – Jason Evans Aug 10 '11 at 20:29
  • \$\begingroup\$ NSpec manages an instance of your test class per specify/it. In context/spec, "it" and "specify" blocks are meant to only assert the given state of a system (not change it). Manipulating state in those blocks is not recommended. As Matt and I tighten down on isolating specification blocks, it'll cause any state defined in these blocks to be reset. Also, there is no guarantee that it and specify blocks will run in the order they have been defined. \$\endgroup\$ – Amir Aug 16 '11 at 18:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.