c# - Testing controller with xUnit
问题描述
I have webapi where it needs to call some other endpoint and get data.
My current code as follows
//http client implementation
public interface IHttpClientFactory
{
HttpClient Create();
}
public class HttpClientFactory : IHttpClientFactory
{
private readonly ApplicationSettings _applicationSettings;
HttpClient _httpClient;
public HttpClientFactory(IOptions<ApplicationSettings> settings)
{
_applicationSettings = settings.Value;
}
public HttpClient Create()
{
if (_httpClient != null)
return _httpClient;
var client = new HttpClient()
{
BaseAddress = new Uri($"{_applicationSettings.BaseUrl}")
};
_httpClient = client;
return _httpClient;
}
}
public interface IGetItemsQuery
{
Task<IEnumerable<T>> Execute<T>(string url);
}
public class GetItemQuery: IGetItemsQuery
{
private readonly IHttpClientFactory _httpClientFactory;
public GetPhotosQuery(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<IEnumerable<T>> Execute<T>(string url)
{
using (var response = await _httpClientFactory.Create().GetAsync($"{url}").ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
var resp = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var items = JArray.Parse(resp);
return items.ToObject<T[]>();
}
}
In my controller part
private readonly IGetItemsQuery _getItemsQuery;
public HomeController(IGetItemsQuery getItemsQuery)
{
_getItemsQuery = getItemsQuery;
}
appsettings
"ApplicationSettings": {
"BaseUrl": "http://someendpoint.com/"
}
Startup
services.Configure<ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
services.AddScoped<IGetItemsQuery, GetPhotosQuery>();
services.AddScoped<IHttpClientFactory, HttpClientFactory>();
I want to try something like below in my test
[Fact]
public void Test_Index()
{
// Arrange
var itemsQuery = new Mock<IGetItemsQuery>();
var controller = new HomeController(itemsQuery.Object);
// Act
var result = controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
Assert.Null(viewResult.ViewName);
}
This is creating mock IGetItemsQuery but this isn't mocking the actual IHttpClientFactory.
Is there a way to do this
解决方案
根据具有抽象依赖项的设计,无需模拟客户端工厂即可对控制器进行单元测试。
正如您在测试中所做的那样,您 mock IGetItemsQuery
,但在测试中调用时,您尚未将其设置为按预期运行。
例如,如果被测控制器方法看起来像这样
private readonly IGetItemsQuery getItemsQuery;
public HomeController(IGetItemsQuery getItemsQuery) {
this.getItemsQuery = getItemsQuery;
}
public async Task<IActionResult> Index() {
var url = "....";
var items = await getItemsQuery.Execute<MyItem>(url);
return View(items);
}
然后,作为被测方法的操作的独立单元Index
测试可能看起来像
[Fact]
public async Task Index_Should_Return_View_With_Items() {
// Arrange
var itemsQuery = new Mock<IGetItemsQuery>();
var items = new MyItem[] {
new MyItem(),
new MyItem()
};
itemsQuery.Setup(_ => _.Execute<MyItem>(It.IsAny<string>()))
.ReturnsAsync(items);
var controller = new HomeController(itemsQuery.Object);
// Act
var result = await controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
Assert.Null(viewResult.ViewName);
}
推荐阅读
- c# - 使用 Visual Studio 调试 dotnet.exe 进程崩溃
- excel - 范围不会更改多个单元格
- python - 如何继承 int 并使用 isinstance 来识别它的实例?
- javascript - 如何在nodejs中打印JSON数据
- docker - 有没有办法防止覆盖 docker 映像中的文件?
- java - 文件扫描程序无限期挂起
- google-apps-script - Google Apps 脚本中的 makeCopy() 方法在我的驱动器而不是客户端驱动器中创建一个副本
- r - 是否有必要使用“。” purr map() 函数中的符号
- javascript - 从自定义插件 php 填充列表项
- android - Android 9 - BluetoothAdapter startDiscovery() 未找到以前找到的设备