3

As a BDD and MSpec beginner I am still not so sure about best practises and good habits related to BDD in general and specifically to MSpec.

Can the following example be improved? Does it follow best practises and good habits?

  1. Is the naming of my spec classes and behavior OK?
  2. Should I use behaviors in this scenario or should I use a common base class for the spec classes ?
  3. Is it ok to not have an Establish here?
  4. Should I use static factory methods (TestData methods) to get test data or should the data be created in the spec itself?
  5. Instead of testing each property in the behavior I could use result.Equals() but then I would test two things, which is not good, right?

Please feel free to refactor the example to something that you would say is better.

[Subject(typeof(DataItemReader))]
public class When_reading_a_DataItem_from_stream
{
    Because of = () =>
    {
        using (var reader = new DataItemReader(
            new MemoryStream(TestData.GetNormalDataItemAsByteArray()), Encryption.None))
        {
            result = reader.ReadItem();
        }
    };

    Behaves_like<DataItemReader_that_reads_correctly> behavior;

    protected static DataItem result;
}

[Subject(typeof(DataItemReader))]
public class When_reading_a_DataItem_from_encrypted_stream
{
    Because of = () =>
    {
        using (var reader = new DataItemReader(
            new MemoryStream(TestData.GetNormalDataItemAsByteArrayEncyrpted()), Encryption.Default))
        {
            result = reader.ReadItem();
        }
    };

    Behaves_like<DataItemReader_that_reads_correctly> behavior;

    protected static DataItem result;
}

[Behaviors]
public class DataItemReader_that_reads_correctly
{
    protected static DataItem result;

    It should_read_the_correct_DataItem = () =>
    {
        var testItem = TestData.GetNormalDataItem();
        result.Property1.ShouldEqual(testItem.Property1);
        result.Property2.ShouldEqual(testItem.Property2);
        result.Property3.ShouldEqual(testItem.Property3);
    };
}
bitbonk
  • 48,890
  • 37
  • 186
  • 278

1 Answers1

6

It's probably less of what the world thinks of the specifications and more of what you and your team/peers/"person who has to read this code after you" can gain from them.

From one dev's perspective:

  1. My casing and naming follows what's readable in code. The nice thing about the HTML extracts is you get readable specs in the end. A lot of what I've read focuses on having all lower case or such; however, I word mine much like yours: proper, readable casing.

  2. For one-to-ones like this, behaviors are great for readability. I use base classes for establishing context and setting up expectations and behaviors for repetative, common assertions.

  3. Establish has always meant "setting up the environment before my test". In the two examples you have, I'd probably rewrite it like:

    Establish context = () => var reader = 
           new DataItemReader(new MemoryStream(     
           TestData.GetNormalDataItemAsByteArray()),                               
           Encryption.None));
    
    Because of = () => result = reader.ReadItem();
    
    Cleanup after = () => reader.Dispose();
    

    Since the specification is focusing on a DataItem "that reads", the action or Because is just that. Again, matter of preference.

  4. I use reusable static factories where I've either handcreated stubs or have a stub/mocking engine (like FakeItEasy http://code.google.com/p/fakeiteasy/). In my mind, the content/creation of stubs has little bearing on the actual test and should be treated by the stubs somewhat as blackbox (that's why we're writing the tests, right?).

  5. I focus on each property individually (as you have it) to ensure that they meet my expectations. If you ever overrode Equals, you'd potentially be checking equality on properties that were not part of the specifications or irrelevant.

I'm not sure there are any governing 'best practices' (there are a few tips on the github site: https://github.com/machine/machine.specifications#readme). I've found a few of my coding styles have changed by looking at other projects using MSpec and observing how they handle their specifications.

David R. Longnecker
  • 2,897
  • 4
  • 29
  • 28
  • Can you suggest some projects that use MSpec that I could study? – bitbonk Aug 10 '11 at 07:22
  • For 3. I would need to call `reader.Dispose()` somewhere. Should I do it here: `Because of = () => try { result = reader.ReadItem(); } finally { reader.Dispose(); }` – bitbonk Aug 10 '11 at 08:43
  • 1
    Off the top of my head, Fluent NHibernate (https://github.com/jagregory/fluent-nhibernate) uses MSpec. A good way to find projects would probably be to hit up github and do a search for Code/C# and 'using Machine.Specifications;' to find all projects with the reference. :) – David R. Longnecker Aug 10 '11 at 12:48
  • Re: Dispose, I believe you can use the Cleanup delegate for that. I've updated my post above with how I _think_ it'd work, but I've never had a live source to dispose in my specs. – David R. Longnecker Aug 10 '11 at 12:49