Reputation: 5047
I have read that you need to define interfaces in order to mock types but I am not sure how to interpret that.
For example, to mock FileSystem, instead of directly calling some I/O method, I can pass the object to the method and then mock it when calling from my test.
Why are unit test examples (like the one from SO question below), use interfaces?
void DoIt(IZipper zipper, IFileSystem fileSystem, IDllRunner runner)
{
string path = zipper.Unzip(theZipFile);
IFakeFile file = fileSystem.Open(path);
runner.Run(file);
}
Could I not simply use arguments of respective types, and inject the object then?
E.g. in the GUICE guide, they advise to remove constructor work (e.g. no field inicialization with "new" keyword), but use parameters istead.
Upvotes: 5
Views: 3220
Reputation: 6901
It is not must to have interface as dependency but that's recommended and widely followed practice.
Using interface and base classes as dependency helps you creating lossely couple design. Loosely coupled design makes it possible to create modules/layers of application without depending on the implementation details about other layer. This also makes it super easy to test the piece of code in isolation without worrying about how other layer would work or react. This type of testing is called unit testing.
If you have dependency on concrete class then you can't unit test code in isolation. Such tests are prone to fail whenever implementation of dependency changes. If your code is dependent on actual file system class or data access class which connects to actual db then there are chances that the unit tests will fail when file is not accessible or db server is down or data is currupted.
This even won't be called unit testing. It is called integration testing.
As I mentioned, while testing code in isolation you don't need to worry about how the dependency works. That's why, while unit testing the dependencies are mocked. Also dependencies must not be an interface. It can be a base class too. When you mock dependency, mocks do not have actual implementation of any of the functionality. How many ways you can create mocks, is a different topics altogether.
Using mocks, in unit testing, you just need to make sure that they behave per your expectations while you test the code your current class. So you ask the mock to behave the way you want, make sure that certain methods or properties of mock called, so that you can cover all the paths of the piece of code which you are testing.
Following is a simple example of Unit Testing with dependency.
public class ProductService
{
private IProductRepository repository;
public ProductService (IProductRepository repository)
{
this.repository = repository;
}
public Product GetProductById(int productId)
{
if(productId <= 0)
return null;
var product = this.repository.GetProductById(productId);
return product;
}
}
While Creating ProductService class, even if I don't have actual implementation of IProductRepository interface, I can still unit test ProductService class as following.
public class ProductServiceTests
{
ProductService serviceToTest;
IProductRepository productRepository;
public ProductServiceTests()
{
this.productRepository = Mock<IProductRepository>();
this.serviceToTest = new ProductService(productRepository);
}
public void GetProductByIdReturnsNullIfProductIdIsZero()
{
int productid = 0;
var product = serviceToTest.GetProductById(productid);
Assert.That(product, Is.Null);
}
public void GetProductByIdReturnsNullIfProductIdIsNegative()
{
int productid = -1;
var product = serviceToTest.GetProductById(productid);
Assert.That(product, Is.Null);
}
public void GetProductByIdReturnsProductIfProductIdIsPositive()
{
var productid = 1;
var product = new Product { Id = productId };
productRepository.Setup(repo => repo.GetProductById(productid)).Returns(product); //Making sure that GetProductById method of repository is called and return an object of product as this call is part of the code which I am testing right now.
var product = serviceToTest.GetProductById(productid);
Assert.That(product, Is.Not.Null);
Assert.That(product.Id, Is.EqualTo<int>(productid));
}
}
This is an example code to illustrate the unit testing. It might not build or run as expected and I apologies for that.
Found an excellent article on mocking classes https://www.wrightfully.com/how-net-mocking-frameworks-work
This should clear doubts of how class mocking is different than the interface mocking.
Upvotes: 4
Reputation: 3703
It depends on your mocking framework. Using Moq, you can mock concrete types as long as they aren't sealed and the members you want to mock are virtual. Interfaces are generally preferred because their members don't need to (and can't) be marked as virtual. (There are other reasons to prefer interfaces such as to keep the coupling between your classes loose)
https://github.com/Moq/moq4/wiki/Quickstart
Update
Generally you will mock an instance of a type. Therefore you can't test static method calls like FileStream.Open because there is no instance to mock.
Update
Frameworks like Moq rely on inheritance to generate the mocks. That's why methods need to be virtual. If you want to mock static or private methods, try Microsoft Fakes.
https://msdn.microsoft.com/en-us/library/hh549175.aspx
Upvotes: 4