0

I use NUNIT and mostly Test with ValueSource to do assertions.

But i never try mocking.

I find a post os SO - What is Mocking?

It said :

To give an example: You can stub a database by implementing a simple in-memory structure for storing records. The object under test can then read and write records to the database stub to allow it to execute the test. This could test some behavior of the object not related to the database and the database stub would be included just to let the test run.

If you instead want to verify that the object under test writes some specific data to the database you will have to mock the database. Your test would then incorporate assertions about what was written to the database mock.

So, is it mean mocking framework act as a virtual database for read/write to satisfy the need of data source?

Community
  • 1
  • 1
Cheung
  • 14,537
  • 17
  • 60
  • 90

1 Answers1

3

Not really. A mocking framework is something that allows you to generate mocks. A mock is generally an object that you can switch for a concrete implementation of something for test purposes. The mock will record which calls are made to it, and it can be configured to return specific values (or throw exceptions) when methods are called on it. Mocks can usually only be created for interfaces or abstract classes, so in order for you to reap the benefits of a mocking framework, your code base will have to adhere to some common OO best practices, such as:

Program to an 'interface', not an 'implementation'

Not making sense? Lets see an example.

Say you have class that fetches some webpage and determines if the page contains the word "hello". It could look like this:

public class HelloFinder
{
    public HelloFinder()
    {

    }

    public bool HasWord(string url)
    {
        var client = new WebClient();
        try
        {
            var result = client.DownloadString(url);

            return result.Contains("hello");
        }
        catch (Exception)
        {
            return false;
        }
    }
}

So how would you test this function, if your test machine was not connected to the internet? You could not, because client.DownloadString would always throw an exception. This would be the same for other types of functions that require specific data in a database to work. They cannot be tested using mocks because they do not adhere to the "Program to an interface not an implementation" practice. So how can we change this? Well, we could make the code that provides the HasWord method with the string to search into a dependecy of the HelloFinder class, so that we can mock it:

public interface IStringDownloader
{
    string DownloadString(string url);
}

public class StringDownloader: IStringDownloader
{
    public string  DownloadString(string url)
    {
        var client = new WebClient();
        return client.DownloadString(url);
    }
}

public class HelloFinder
{
    private readonly IStringDownloader downloader;
    public HelloFinder(IStringDownloader downloader)
    {
        if (downloader == null) throw new ArgumentNullException("downloader");

        this.downloader = downloader;
    }

    public bool HasWord(string url)
    {
        try
        {
            var result = this.downloader.DownloadString(url);

            return result.Contains("hello");
        }
        catch (Exception)
        {
            return false;
        }
    }
}

This code does exactly the same as the example above, but it allows us to test the HasWord method without worrying about internet connections. By using a mock instead of the actual implementation (StringDownloader).

The code could be used like this in your live application:

var finder = new HelloFinder(new StringDownloader());
var googleHasHello = finder.HasWord("http://www.google.com");

But in order to test the HasWords method we don't really care whether the string comes from google.com or anywhere else. We just need to test that given any string it behaves correctly, and that when the downloader throws an exception it also behaves correctly. And this is where the mock comes into play. Personally I use the mocking framework called Moq so the examples here use that aswell. Here is a test using a mock:

public void TestThatFinderWillReturnTrueWhenHelloIsInString()
{
    var downloaderMock = new Mock<IStringDownloader>();
    downloaderMock.Setup(d => d.DownloadString(It.IsAny<string>())).Returns("A string that has hello in it");

    var finder = new HelloFinder(downloaderMock.Object);

    var result = finder.HasWord("any string will do");

    //Assert that result is true
}

So what is going on here? First we create a new Mock and ask it to mimic the IStringDownloader interface. We then call the Setup method to tell to mock to return a hardcoded string every time the DownloadString method is called, and we also tell it not to care what the argument is (achieved by the It.IsAny<string>() argument. We now have a mock that behaves as we want it to, so we use it for the creation of the HelloFinder class and call the CountWords method. To conclude the test, all we have to do is to check that result is correct.

Other useful tests for this class could be:

public void TestThatFinderWillCallCorrectMethod()
{
    var downloaderMock = new Mock<IStringDownloader>();
    downloaderMock.Setup(d => d.DownloadString(It.IsAny<string>()))
        .Returns("A string that has hello in it").Verifiable();

    var finder = new HelloFinder(downloaderMock.Object);

    var result = finder.HasWord("any string will do");

    downloaderMock.Verify();
}


public void TestThatFinderWillReturnFalseWhenHelloIsNotInString()
{
    var downloaderMock = new Mock<IStringDownloader>();
    downloaderMock.Setup(d => d.DownloadString(It.IsAny<string>())).Returns("A string that does not have h3llo in it");

    var finder = new HelloFinder(downloaderMock.Object);

    var result = finder.HasWord("any string will do");

    //Assert that result is false
}

public void TestThatFinderWillReturnFalseWhenDownloaderThrowsAnException()
{
    var downloaderMock = new Mock<IStringDownloader>();
    downloaderMock.Setup(d => d.DownloadString(It.IsAny<string>())).Throws(new Exception());

    var finder = new HelloFinder(downloaderMock.Object);

    var result = finder.HasWord("any string will do");

    //Assert that result is false
}

To sum up

So back to your original question. Is a mocking framework something that acts like a virtual database? Not exactly. But it is something that you make act like anything you want. Do you need mocking for your unittests? Well, they are certainly helpful, but they require that your code is written in a certain way. Projects that use dependency injection are very easily tested with mocks. But since I don't know about your specific code base, it's hard to say. You say that you already have some unit tests. These would have to be rewritten to use mocks, and your entire code base may also have to be rewritten so that testing with mocks is even possible. So whether mocks are a good idea for your current project is up to you to decide. But make sure to have them in mind for future projects.

I hope this shed some light over the issue.

Klaus Byskov Pedersen
  • 104,458
  • 27
  • 176
  • 219
  • Thanks you for detail explanation with great sample.Base on your answer, is it mean the definition of "mocking" is test the "HasWords() is work on different scenario" rather than "HasWords() contains "hello" ? – Cheung Dec 02 '11 at 16:39
  • I find 1 post on SO is worth to read : http://stackoverflow.com/questions/4314/what-is-object-mocking-and-when-do-i-need-it – Cheung Dec 02 '11 at 16:58
  • @SilverNight yes, more or less. But think of a mock as a "test double". Someting that is put instead of the real thing. Afterall, when you test a class, you only want to test **that** class, not all the other classes (they should be tested somewhere else). The point of the above code is that when you test the `HelloFinder` class you **only** test that. You do not need to test whether Microsoft's `WebClient` does what it does, because, presumably, it works as documented. Mocking allows you to test a class' functionality, regardless of the internal workings of its dependencies. – Klaus Byskov Pedersen Dec 02 '11 at 23:39
  • Thanks you very much! Now i have more understand about mocking.You are Hero! – Cheung Dec 03 '11 at 01:26