Using interfaces has another benefit: it facilitates mocking.
To evaluate whether your code is good, properly separated, or over-designed, write comprehensive unit tests for yourself. If it's difficult or impossible to write unit tests for your own code, then you should consider refactoring it.
First, inject mocks into the unit test class:
public class BasketWebApiTest
{
private readonly Mock<IBasketRepository> _basketRepositoryMock;
private readonly Mock<IBasketIdentityService> _identityServiceMock;
private readonly Mock<IEventBus> _serviceBusMock;
private readonly Mock<ILogger<BasketController>> _loggerMock;
public BasketWebApiTest()
{
_basketRepositoryMock = new Mock<IBasketRepository>();
_identityServiceMock = new Mock<IBasketIdentityService>();
_serviceBusMock = new Mock<IEventBus>();
_loggerMock = new Mock<ILogger<BasketController>>();
}
}
Next, start mocking a method and interacting with the Controller for testing.
[Fact]
public async Task Get_customer_basket_success()
{
//Arrange
var fakeCustomerId = "1";
var fakeCustomerBasket = GetCustomerBasketFake(fakeCustomerId);
_basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny<string>()))
.Returns(Task.FromResult(fakeCustomerBasket));
_identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId);
_serviceBusMock.Setup(x => x.Publish(It.IsAny<UserCheckoutAcceptedIntegrationEvent>()));
//Act
var basketController = new BasketController(
_loggerMock.Object,
_basketRepositoryMock.Object,
_identityServiceMock.Object,
_serviceBusMock.Object);
var actionResult = await basketController.GetBasketByIdAsync(fakeCustomerId);
//Assert
Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK);
Assert.Equal((((ObjectResult)actionResult.Result).Value as CustomerBasket).BuyerId, fakeCustomerId);
}
You can also mock function parameters:
_basketServiceMock.Setup(x => x.AddItemToBasket(It.IsAny<ApplicationUser>(), It.IsAny<int>()))
.Returns(Task.FromResult(1));
When services are normally injected, you can simply use new Controller
, but if the Controller depends on HttpContext (which can be used without dependency injection), it can inherit from ControllerBase.
[Controller]
public abstract class ControllerBase
{
public HttpContext HttpContext
public HttpRequest Request
public HttpResponse Response
public RouteData RouteData
public ModelStateDictionary ModelState
[ControllerContext]
public ControllerContext ControllerContext
public IModelMetadataProvider MetadataProvider
public IModelBinderFactory ModelBinderFactory
public IUrlHelper Url
public IObjectModelValidator ObjectValidator
public ProblemDetailsFactory ProblemDetailsFactory
public ClaimsPrincipal User
}
Thus, in addition to mocking injected services, you also need to mock some properties of ControllerBase.
//Act
var orderController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object);
orderController.ControllerContext.HttpContext = _contextMock.Object;
var actionResult = await orderController.AddToCart(fakeCatalogItem);
Unit test names can be longer:
public async Task Get_get_all_catalogitems_and_response_ok_status_code()
public async Task Get_get_catalogitem_by_id_and_response_ok_status_code()
public async Task Get_get_catalogitem_by_id_and_response_bad_request_status_code()
public async Task Get_get_catalogitem_by_id_and_response_not_found_status_code()
public async Task Get_get_catalogitem_by_name_and_response_ok_status_code()
public async Task Get_get_paginated_catalogitem_by_name_and_response_ok_status_code()
If you are only testing that no errors occur without checking return values, you can write it as follows:
using (var server = CreateServer())
{
var response = await server.CreateClient()
.GetAsync("api/v1/catalog/catalogtypes");
response.EnsureSuccessStatusCode();
}
文章评论