I've been learning about BDD and after trying out a few frameworks have decided to use MSpec in my latest project.

After looking through some examples, I'm unsure about how to identify the scenario and context.

Take the following story (taken from Rob Connery's Kona example):

Removing a shopping cart item
    * All items of same SKU are removed. TotalItems decremented by Quantity
    * When quantity is 0 nothing happens
    * When quantity is negative, items are removed, TotalItems stays at 0 (never negative)

And here is the associated spec:

[Subject("Cart with items in it")]
public class when_removing_item : with_cart_with_1_item_of_sku1 {
    It should_remove_all_items_with_same_sku;
    It should_not_remove_anything_when_sku_not_in_cart;
    It should_not_remove_more_items_than_are_in_cart_resulting_in_negative_totalitems;
}

Now, if my understanding is correct:

  • Scenario => Cart with items in it
  • Context => cart with 1 item of sku1
  • Specification => Removing an item
  • Assertion => Should remove all items with the same sku

However, looking at other examples it seems that it should be declared like so:

  • Scenario => Removing an item to cart
  • Context => When the cart has 1 item in it
  • Specification => Removing an item
  • Assertion => Should remove all items with the same sku

and the test should be:

[Subject("Removing an item from cart")]
public class when_removing_an_item_from_cart_with_items : with_cart_with_1_item_of_sku1 {
    It should_remove_all_items_with_same_sku;
    // etc.
}

Is my understanding correct, or is there no right and wrong method? My assumption was that the Subject/Scenario relates to the overall behaviour we are testing (i.e. removing an item from a cart) and each specification class tests that behaviour under different contexts.

有帮助吗?

解决方案

Ben, I would argue the second example you give is more "correct". When you think about contexts (when class), make sure that is contains everything that is associated with the scenario. Most times there's only one "action" (in your example, removing an item) with specifications expressing the state the system should be in after that action took place in the specific context. Of course there can be different preconditions (cart empty, cart with one item, etc), these form different contexts.

Just to clarify, removing an item is not an observation, it's the action.

Hence, I think you could write the following specs for the feature outlined:

[Subject("Removing item from shopping cart")]
public class when_removing_item_from_empty_cart {
  It should_be_empty;
}

[Subject("Removing item from shopping cart")]
public class when_removing_item_from_cart_with_one_item_of_another_SKU {
  It should_still_contain_the_item;
}

[Subject("Removing item from shopping cart")]
public class when_removing_item_from_cart_with_two_items {
  It should_be_empty;
}

// probably more ;-)

Use the SubjectAttribute to denote the feature/scenario. You can also use TagsAttribute to add more metadata (and possibly filter test runs).

I would deliberately choose not to use base classes. From my experience, there are better ways to even do complex setup (Fluent API, Machine.Fakes behavior configs). DRY doesn't really apply here: When a spec fails, you want to have everything that belongs to the spec right in front of you, instead of navigating class hierarchies to look for Establishs that might linger somewhere else.

Also, you might find this sample app interesting, developed and tested with MSpec.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top