Can this MSpec test be improved?
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?
- Is the naming of my spec classes and behavior OK?
- Should I use behaviors in this scenario or should I use a common base class for the spec classes ?
- Is it ok to not have an
Establish
here? - Should I use static factory methods (
TestData
methods) to get test data or should the data be created in the spec itself? - 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);
};
}
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:
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.
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.
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.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?).
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.
精彩评论