You are focusing too much on implementation concerns. In this scenario entity framework is an implementation concern.
This looks like a good case for encapsulating that concern out into an abstraction.
public interface IProductService {
Task<List<Product>> GatherProductInfo(int customerId);
}
and injecting that into the controller
public class ProductsController : Controller {
private readonly IProductService service;
public ProductsController(IProductService service) {
this.service = service;
}
public async Task<IActionResult> Get(int customerId) {
List<Product> products = await service.GatherProductInfo(customerId);
if (!products.Any()) {
return NotFound();
}
return Ok(products);
}
}
The IProductService
implementation would depend on the context and the actual stored procedure execution while the controller only depends on the abstraction. The controller should not concern itself with where the data comes from.
This now allows the controller to be unit tested in isolation without tight coupling to implementation concerns like entity framework.
public async Task Product_Not_Found() {
//Arrange
var customerId = 1;
var products = new List<Product>();// Currently empty but could easily
// be populated for another test.
var mock = new Mock<IProductService>();
mock.Setup(_ => _.GatherProductInfo(customerId)).Returns(products);
var controller = new ProductsController(mock.Object);
//Act
var result = await controller.Get(customerId);
//Assert
result.Should().NotBeNull()
.And.BeTypeOf<NotFoundResult>();
}