Reputation:
I have a simple entity framework repository that is accessed via a service call (please refer to this UML diagram to see the relevant classes). I am trying to test the service as follows
public class ProductServiceTests
{
private IProductService sut;
private IProductRepository productRepo;
[Fact]
public async Task CanCallGetProductById()
{
//Arrange
productRepo = new Fake<IProductRepository>().FakedObject;
sut = new ProductService(productRepo);
var id = 1;
var expectation = new Product { Id = id };
A.CallTo(() => sut.GetProductById(id)).Returns(expectation);
// Act
var result = await sut.GetProductById(id);
// Assert
Assert.Equal(id, result.Id);
}
}
However, this leads to the following exceptionSystem.ArgumentException : Object 'ProductService' of type ProductService is not recognized as a fake object.
Which implies that I probably need to use a mock of the service as follows
sut = new Fake<IProductService>().FakedObject;
This approach, however, is also problematic: the call to the function await sut.GetProductById(id)
does not actually invoke any code under test. Indeed, if I throw an exception, the test still passes
public async Task<Product> GetProductById(int id)
{
throw new NotImplementedException();
//return await _productRepository.GetAll().FirstOrDefaultAsync(x => x.Id == id);
}
Edit After some troubleshooting, I realized that, indeed, the code I am trying to test is not actually reached. For example, the following test
A.CallTo(() => productRepo.GetAll()).MustHaveHappened();
fails with a message that Product.GetAll() Expected to find it once or more but no calls were made to the fake object.
Can please someone explains how I should fix this error?
Edits
The IProductRepository
and IProductService
are defined as follows:
public interface IProductRepository
{
IQueryable<Product> GetAll();
}
public interface IProductService
{
Task<Product> GetProductById(int id);
}
And the ProductService
is implemented as follows
public class ProductService : IProductService
{
private readonly IProductRepository _productRepository;
public ProductService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task<Product> GetProductById(int id)
{
//throw new NotImplementedException();
return await _productRepository.GetAll()
.FirstOrDefaultAsync(x => x.Id == id);
}
}
Upvotes: 1
Views: 2940
Reputation: 241980
The general approach when using fakes (or mocks or whatever) to help test a service is to mock the system under test's collaborators (in this case, IProductService
), and to use a real system under test (if you don't use a real system under test, your system isn't "under test"). So, taking your original code and adapting, you'd want something like this:
public class ProductServiceTests
{
private IProductService sut;
private IProductRepository productRepo;
[Fact]
public async Task CanCallGetProductById()
{
//Arrange
productRepo = new Fake<IProductRepository>().FakedObject;
sut = new ProductService(productRepo);
var id = 1;
var expectation = new Product { Id = id };
// This is different from your example.
// The idea is to configure `productRepo`, not `sut`.
// Also, I don't know the interface for `IProductRepository`,
// so you'll have to adjust this call to match `GetAll`
A.CallTo(() => productRepo.GetAll(id)).Returns(new [] {expectation});
// Act
var result = await sut.GetProductById(id);
// Assert
Assert.Equal(id, result.Id);
}
}
Upvotes: 1