次の方法で共有


Exit Mocks. Enter Stubs!

Since I'm not a fan of mocks I guess my prayers have been heard. Microsoft Research will soon release a stub framework. That's right. Stubs and not mocks! It is part of Pex that I have mentioned before. And you don't need to use Pex if you don't want. You can just use the stub framework by it self.

The new stub framework can currently be used to stub interfaces. It creates the stubs by generating code. At first this scared me since I feared it would not regenerate the stubs when the interface changed (which might happen quite common when creating the interface using BDD). But it works just fine and detects changes in the assembly and regenerates the stubs. Very convenient.

Another convenient thing is that it supplies default implementations for methods if I want. The default is to throw an StubNotImplementedException but I can change that and return default values (null, 0, empty string etc) if that is what I want. And the change can be made globally for all stubs created after the change or for a single stub object.

So how does this work? Consider the following interface:

 public interface IMyInterface
{
    int DoStuff();
}

I just tell the stub framework to generate stubs for the assembly containing the interface and it will generate a new namespace with the stubs. If IMyInterface is part of MyNamespace the stub framework will create a new namespace MyNamespace.Stubs with a class SMyInterface. Now we want to make a test. The test below doesn't really make since since it tests the stub instead of something using the stub, but I want to show the code that makes the stub work without the surrounding stuff so bare with me...

 void TestStub()
{
    MyNamespace.Stubs.SMyInterface o = new MyNamespace.Stubs.SMyInterface();
    MyNamespace.IMyInterface i = o;
    Assert.Equal(42, i.DoStuff());
}

Now the test will fail since the stub will throw an exception since the stub is not implemented. Let's use the default implementation:

 void TestStub()
{
    MyNamespace.Stubs.SMyInterface o = new MyNamespace.Stubs.SMyInterface();
    o.DefaultStub = Microsoft.Stubs.DefaultStub<MyNamespace.Stubs.SMyInterface>.DefaultValue;
    MyNamespace.IMyInterface i = o;
    Assert.Equal(42, i.DoStuff());
}

Still a failure since the stub returns zero for DoStuff(). Let's stub that method.

 void TestStub()
{
    MyNamespace.Stubs.SMyInterface o = new MyNamespace.Stubs.SMyInterface();
    o.DefaultStub = Microsoft.Stubs.DefaultStub<MyNamespace.Stubs.SMyInterface>.DefaultValue;
    o.DoStuff = (stub) => { return 42; };
    MyNamespace.IMyInterface i = o;
    Assert.Equal(42, i.DoStuff());
}

Now the test passes! I think this new framework is an excellent addition to the developer's toolbox. And I think it is a better starting framework than all the mock frameworks out there. In the few cases that a mock framework is really needed (over a stub framework) you can easily extend your stub to verify behavior in the same way as a mock does. Currently the stub framework have a few limitations such as only being able stub interfaces. But I'll guess we'll have to wait and see what will be part of the release once it is available to the public (when writing this I've been using an internal release available to Microsoft staff only).

Comments

  • Anonymous
    October 14, 2008
    Abstract classes and abstract methods are also supported. We have a work item to make it work for non-sealed classes and virtual methods.

  • Anonymous
    October 14, 2008
    The comment has been removed