Last week I talked about Rubifying your C# code by adding the .Each() extension method to the IEnumerable<T> interface. I was discussing that code with one of the guys at work and he asked a brilliant question
So, how did you test that?
I was floored. Being such a test-first fan boy I couldn’t believe that I had failed to write that little extension method in a TDD manner. Hell, I hadn’t even done TAD!
So how do I test it?
There are no doubt a plethora of ways to test this small bit of code - some right and many wrong. In the end I turned to my good friend Rhino.Mocks to do some expectation, or interaction, based testing.
If you don’t fully grok mocks, and in particular Rhino.Mocks, the following code might look a little foreign. I’ll try to explain the basics of what’s going on in the below code snippet, but be warned I’ll be doing a little bit of hand-waving to make the code more soluble. I’ll be doing a much deeper dive in some future posts, so stay tuned!
1: public class When_Executing_Each_On_IEnumerable_That_Has_Items : SpecBase
2: {
3: [Test]
4: public void Should_Executes_CodeBlock_On_Every_Item()
5: {
6: ITestWidget myWidget = CreateMock<ITestWidget>();
7: IEnumerable<ITestWidget> widgets =
8: new ITestWidget[] { myWidget, myWidget, myWidget, myWidget, myWidget };
9:
10: using (Record)
11: {
12: Expect.Call(myWidget.Foo()).Return("I was called!").Repeat.Times(5);
13: }
14:
15: using (Playback)
16: {
17: widgets.Each(w =>
18: {
19: string output = w.Foo();
20: Console.WriteLine(output);
21: });
22: }
23: }
24: }
25:
26: public interface ITestWidget
27: {
28: string Foo();
29: }
All of the above hand-waving is really just adding some syntactic sugar around use of the Rhino.Mocks MockRepository class, and its all done in the SpecBase class which you can get via the link at the end of this post.
So, what am I expecting?
In lines 6-8 I created a mock of the ITestWidget interface and then built an array that contained that object. Well, really it contains 5 references to that one object. With the array built, I flipped the MockRepository into Record mode so I could tell Rhino.Mocks what I was expecting to happen to my mock object.
10: using (Record)
11: {
12: Expect.Call(myWidget.Foo()).Return("I was called!").Repeat.Times(5);
13: }
I told Rhino.Mocks to Expect a Call to the myWidget object’s Foo method. And when that method is called I want the mock to Return the string "I was called!". Oh, and I also expect that method to be called, or Repeated, five (5) Times.
And what is happening?
Once all of my expectations are setup I switch the MockRepository to Playback mode, meaning I’m ready to exercise the expectations I setup during Record mode.
15: using (Playback)
16: {
17: widgets.Each(w =>
18: {
19: string output = w.Foo();
20: Console.WriteLine(output);
21: });
22: }
Once in playback mode I simply need to exercise the SUT... or in this case the one particular method I’m concerned with. Into that method I’m passing a Lambda expression with a code block that will actually execute the method I’ve set the expectation on.
What happens when I run the test?
I get some output, naturally!
Seriously though, if I were doing this test-first, as I should have, the test would have failed because I’d not yet written the code to implement the Each method.
But after adding just the code needed to get the test to pass, and then running the test, I should see all green. Oh, and the console output looks something like
1: I was called!
2: I was called!
3: I was called!
4: I was called!
5: I was called!
Nice! The string I expected to be returned from the method was written out to the console the exact number of times I expected.
Meaning?
Meaning the method behaved just as I expected it to. My test... or my Spec in BDD parlance, specified how the method should behave and then verified that it behaved as specified.
See what I did there? BDD isn’t sooo scary, right?*
Get the code
You can get the complete source code in one of two ways
- The code as it existed at the time I wrote this post.
- The living code, including any changes and complete version history, from the CodeIncubator project’s Subversion repository.
I’d recommend pulling down the live code directly out of the SVN repository to be sure to get the latest and greatest bits. We, or at least I, plan to keep adding bits and use the project to stretch my BDD legs.
*Note: I’ve only recently started dipping my toes in the BDD waters, but so far I’m really liking what I’m seeing!