Best Practices of Test Driven Development Using C# and RhinoMocks

28,733

Solution 1

Definitely a good list. Here are a few thoughts on it:

Write the test first, then the code.

I agree, at a high level. But, I'd be more specific: "Write a test first, then write just enough code to pass the test, and repeat." Otherwise, I'd be afraid that my unit tests would look more like integration or acceptance tests.

Design classes using dependency injection.

Agreed. When an object creates its own dependencies, you have no control over them. Inversion of Control / Dependency Injection gives you that control, allowing you to isolate the object under test with mocks/stubs/etc. This is how you test objects in isolation.

Separate UI code from its behavior using Model-View-Controller or Model-View-Presenter.

Agreed. Note that even the presenter/controller can be tested using DI/IoC, by handing it a stubbed/mocked view and model. Check out Presenter First TDD for more on that.

Do not write static methods or classes.

Not sure I agree with this one. It is possible to unit test a static method/class without using mocks. So, perhaps this is one of those Rhino Mock specific rules you mentioned.

Program off interfaces, not classes.

I agree, but for a slightly different reason. Interfaces provide a great deal of flexibility to the software developer - beyond just support for various mock object frameworks. For example, it is not possible to support DI properly without interfaces.

Isolate external dependencies.

Agreed. Hide external dependencies behind your own facade or adapter (as appropriate) with an interface. This will allow you to isolate your software from the external dependency, be it a web service, a queue, a database or something else. This is especially important when your team doesn't control the dependency (a.k.a. external).

Mark as virtual the methods you intend to mock.

That's a limitation of Rhino Mocks. In an environment that prefers hand coded stubs over a mock object framework, that wouldn't be necessary.

And, a couple of new points to consider:

Use creational design patterns. This will assist with DI, but it also allows you to isolate that code and test it independently of other logic.

Write tests using Bill Wake's Arrange/Act/Assert technique. This technique makes it very clear what configuration is necessary, what is actually being tested, and what is expected.

Don't be afraid to roll your own mocks/stubs. Often, you'll find that using mock object frameworks makes your tests incredibly hard to read. By rolling your own, you'll have complete control over your mocks/stubs, and you'll be able to keep your tests readable. (Refer back to previous point.)

Avoid the temptation to refactor duplication out of your unit tests into abstract base classes, or setup/teardown methods. Doing so hides configuration/clean-up code from the developer trying to grok the unit test. In this case, the clarity of each individual test is more important than refactoring out duplication.

Implement Continuous Integration. Check-in your code on every "green bar." Build your software and run your full suite of unit tests on every check-in. (Sure, this isn't a coding practice, per se; but it is an incredible tool for keeping your software clean and fully integrated.)

Solution 2

If you are working with .Net 3.5, you may want to look into the Moq mocking library - it uses expression trees and lambdas to remove non-intuitive record-reply idiom of most other mocking libraries.

Check out this quickstart to see how much more intuitive your test cases become, here is a simple example:

// ShouldExpectMethodCallWithVariable
int value = 5;
var mock = new Mock<IFoo>();

mock.Expect(x => x.Duplicate(value)).Returns(() => value * 2);

Assert.AreEqual(value * 2, mock.Object.Duplicate(value));

Solution 3

Know the difference between fakes, mocks and stubs and when to use each.

Avoid over specifying interactions using mocks. This makes tests brittle.

Solution 4

The real reason for programming against interfaces is not to make life easier for Rhino, but to clarify the relationships between objects in the code. An interface should define a service that an object needs from its environment. A class provides a particular implementation of that service. Read Rebecca Wirfs-Brock's "Object Design" book on Roles, Responsibilities, and Collaborators.

Solution 5

Good list. One of the things that you might want to establish - and I can't give you much advice since I'm just starting to think about it myself - is when a class should be in a different library, namespace, nested namespaces. You might even want to figure out a list of libraries and namespaces beforehand and mandate that the team has to meet and decide to merge two/add a new one.

Oh, just thought of something that I do that you might want to also. I generally have a unit tests library with a test fixture per class policy where each test goes into a corresponding namespace. I also tend to have another library of tests (integration tests?) which is in a more BDD style. This allows me to write tests to spec out what the method should do as well as what the application should do overall.

Share:
28,733
Kevin Albrecht
Author by

Kevin Albrecht

Favorite languages: Go, Clojure

Updated on July 08, 2022

Comments

  • Kevin Albrecht
    Kevin Albrecht almost 2 years

    In order to help my team write testable code, I came up with this simple list of best practices for making our C# code base more testable. (Some of the points refer to limitations of Rhino Mocks, a mocking framework for C#, but the rules may apply more generally as well.) Does anyone have any best practices that they follow?

    To maximize the testability of code, follow these rules:

    1. Write the test first, then the code. Reason: This ensures that you write testable code and that every line of code gets tests written for it.

    2. Design classes using dependency injection. Reason: You cannot mock or test what cannot be seen.

    3. Separate UI code from its behavior using Model-View-Controller or Model-View-Presenter. Reason: Allows the business logic to be tested while the parts that can't be tested (the UI) is minimized.

    4. Do not write static methods or classes. Reason: Static methods are difficult or impossible to isolate and Rhino Mocks is unable to mock them.

    5. Program off interfaces, not classes. Reason: Using interfaces clarifies the relationships between objects. An interface should define a service that an object needs from its environment. Also, interfaces can be easily mocked using Rhino Mocks and other mocking frameworks.

    6. Isolate external dependencies. Reason: Unresolved external dependencies cannot be tested.

    7. Mark as virtual the methods you intend to mock. Reason: Rhino Mocks is unable to mock non-virtual methods.

  • Kevin Albrecht
    Kevin Albrecht over 15 years
    I also do a similar BDD style testing section (in addition to the unit testing code) in a personal project.
  • Abhishek
    Abhishek over 15 years
    I think the new version of Rhino Mocks works like this too
  • Steve Freeman
    Steve Freeman over 14 years
    I usually find that if a test is hard to read, it's not the fault of the framework but of the code it's testing. If the SUT is complicated to set up, then maybe it should be broken up into more concepts.
  • Kevin Albrecht
    Kevin Albrecht over 14 years
    Agreed... I am going to update my question to reflect that.