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.