views:

169

answers:

4

If I was to write a mocking library, how would this work (in other words, how do "they work?)?

One of the things which I wonder is that you are always setting expectations so really you need to compare the expectation to what the method does at runtime, so I assume reflection (resolving types at runtime) is required.

Also, when using the term "mock object", is the object stubbed out or would it be an object with pre-set expectations?

When I think how I would write my own implementation of a framework/technique, like mock objects, I realise how much I really know (or don't know) and what I would trip up on: If the mock object is pre-programmed to return set expectations and you don't call the actual real object, then wouldn't the result always be the same? Eg:

[TestMethod, Isolated]
public void FakeReturnValueByMethodArgs()
{
    var fake = Isolate.Fake.Instance<ClassToIsolate>();
    // MethodReturnInt will return 10 when called with arguments 3, "abc"
    Isolate.WhenCalled(()=> fake.MethodReturnInt(3,   "     abc")).WithExactArguments().WillReturn(10);
// MethodReturnInt will return 50 when called with arguments 3, "xyz"
    Isolate.WhenCalled(()=> fake.MethodReturnInt(3, "xyz")).WithExactArguments().WillReturn(50);

     Assert.AreEqual(10, fake.MethodReturnInt(3, "abc"));
    Assert.AreEqual(50, fake.MethodReturnInt(3, "xyz"));

}

Wouldn't this always return true?

A: 

Yes, it will always return true. Mock objects should be used when the class under test requires another class implementation that you don't want to involve in the test run. This is most useful when it's a class that uses interfaces with multiple implementations, or there are complex/expensive/external services that you don't want to set up.

In the above code, you're mocking the class that you're "testing".

Another way of thinking about it is that the mock behaviours you record are black-box (implementation) assertions, where Assert.* are white-box (api) assertions.

Stephen
Seen another way, mock objects are just another layer of indirection above the return(true) or commenting you'd have to use in their stead.
Vinko Vrsalovic
A: 

You have the right idea. You will often find that they have a couple of modes of operation. If you're worried about your method not getting called or it not getting called in the right order there is quite often a 'strict' mode that causes the mock framework to throw an exception if the method isn't called by the end of the test, or is called with the wrong parameters etc.

Most of the frameworks have thought of those sorts of issues so you just need to find out how to configure it for your scenario.

Colin Newell
+5  A: 

The idea with mocking frameworks is to mock out dependencies, and not the actual classes under test. For your example, your test will always return true, because really you're only testing the mocking framework and not your actual code!

A real world mock would look more like this:

[TestMethod, Isolated]
public void FakeReturnValueByMethodArgs() {
    var fake = Isolate.Fake.Instance<DependencyClass>();
    // MethodReturnInt will return 10 when called with arguments 3, "abc"
    Isolate.WhenCalled(()=> fake.MethodReturnInt(3, "abc")).WithExactArguments().WillReturn(10);

    var testClass = new TestClass(fake);
    testClass.RunMethod();

    // Verify that the setup methods were execute in RunMethod()
    // Not familiar with TypeMock's actual method to do this...
    IsolatorExtensions.VerifyInstanceWasCalled(fake);  

    // Or assert on values
    Assert.AreEqual(10, testClass.AProperty);
}

Notice how the mock is passed into the TestClass and a method run on it.

You can read The Purpose of Mocking to get a better idea of how mocking works.


Update: Explanation why you're testing only the mocking framework:

What you've done is create a method MethodReturnInt with the mocking framework using Isolate.WhenCalled(). When you call MethodRecturnInt in the Assert, the code will run the delegate () => fake.MethodReturnInt() and return 10. The mocking framework is effectively creating a method (albeit dynamically) that would look something like this:

public void MethodReturnInt(int value, string value2) {
    Assert.Equal(3, value);
    Assert.Equal("abc", value2);
    return 10;
}

It's a bit more complicated than that, but this is the general idea. Since you never run any code other than the creation of 2 methods and then asserts on those two methods, you're not testing your own code and therefore only testing the mocking framework.

Gavin Miller
Thanks. Can you explain how the code I provided tests the mocking framework and the actual code (maybe I am a bit slow)?
dotnetdev
Thanks for that. I may have a method which relies on a certain date/time to perform an action, but what if my method actually returns the current time (the essence of the method is something which is usually mocked)? How do I test this? If a method does something at 12am, I test the "something" it does, but what if my method returns the time, how do I test this?
dotnetdev
@dotnetdev - Take a look at this SO question: http://stackoverflow.com/questions/565289/unit-testing-code-that-does-date-processing-based-on-todays-date/565314#565314 Effectively you need to break the DateTime dependency in your code
Gavin Miller
A: 

One way to look at how mock system work is just look at times when you need an object but you don't want to use the real class but instead want it to give you some specific kind of data that it wouldn't (or won't do so reliably). So if you see:

Assert.IsTrue(myLogic.IsNoon(time))

you can see how the assert would want the time object to always be noon. . . well you can't do that with a real object reliably. So you need a stand-in. You can make a fake class just for the test, but that's sort of heavy. Mock frameworks are a shortcut.

Kevin Won