gpt4 book ai didi

c# - 如何模拟/单元测试 HTTP 客户端 - restease

转载 作者:行者123 更新时间:2023-12-02 02:07:20 28 4
gpt4 key购买 nike

tl;dr:我在模拟 restease 时遇到了问题**

此外,我意识到我可能完全走错了路,所以任何正确方向的建议/插入都会有很大帮助。我对此很陌生。

我正在制作一个围绕 RestEase 构建的小型 HTTP 客户端库. RestEase 很好用且易于使用,但我在模拟调用以进行单元测试时遇到问题。

我想使用最小起订量和 NUnit,但我无法正确模拟 RestClient。示例(为简洁起见缩短):

IBrandFolderApi - restease发送调用需要的接口(interface)

public interface IBrandFolderApi
{
[Post("services/apilogin")]
Task<LoginResponse> Login([Query] string username, [Query] string password);
}

BrandfolderClient.cs - 主类

public class BrandfolderClient : IBrandfolderClient
{
private IBrandFolderApi _brandFolderApi { get; set; }

public BrandfolderClient(string url)
{
_brandFolderApi = RestClient.For<IBrandFolderApi >(url);
}

public async Task<string> Login(string username, string password)
{
LoginResponse loginResponse = await _brandFolderApi .Login(username, password);
if (loginResponse.LoginSuccess)
{
....
}
....
return loginResponse.LoginSuccess.ToString();
}
}

单元测试

public class BrandFolderTests
{
BrandfolderClient _brandfolderClient
Mock<IBrandFolderApi> _mockBrandFolderApii;


[SetUp]
public void Setup()
{
//The test will fail here, as I'm passing a real URL and it will try and contact it.
//If I try and send any string, I receive an Invalid URL Format exception.
string url = "https://brandfolder.companyname.io";
_brandfolderClient = new BrandfolderClient (url);
_mockBrandFolderApii= new Mock<IBrandFolderApi>();
}

....
}

所以,我不知道如何正确模拟 Restclient,使其不会向实际 URL 发送实际请求。

测试在构造函数中失败 - 如果我发送一个有效的 URL 字符串,它就会发送一个对实际 URL 的调用。如果我发送任何其他字符串,我会收到无效的 URL 格式异常。

我相信我还没有在其他客户端周围正确地实现一些东西,但我不确定在哪里。我非常坚持这一点,我一直在谷歌搜索和疯狂阅读,但我错过了一些东西,我不知道是什么。

最佳答案

So, I don't know how to properly mock the Restclient so it doesn't send an actual request to an actual URL.

您实际上不需要模拟 RestClient

重构您的代码以明确依赖于您控制的抽象

public class BrandfolderClient : IBrandfolderClient {
private readonly IBrandFolderApi brandFolderApi;

public BrandfolderClient(IBrandFolderApi brandFolderApi) {
this.brandFolderApi = brandFolderApi; //RestClient.For<IBrandFolderApi >(url);
}

public async Task<string> Login(string username, string password) {
LoginResponse loginResponse = await brandFolderApi.Login(username, password);
if (loginResponse.LoginSuccess) {
//....
}

//....

return loginResponse.LoginSuccess.ToString();
}
}

消除与静态第 3 方实现问题的紧密耦合将使您的主题更明确地说明执行其功能实际需要什么。

这也将使受试者更容易被隔离测试。

例如:

public class BrandFolderTests { 
BrandfolderClient subject;
Mock<IBrandFolderApi> mockBrandFolderApi;

[SetUp]
public void Setup() {
mockBrandFolderApi = new Mock<IBrandFolderApi>();
subject = new BrandfolderClient(mockBrandFolderApi.Object);
}

//....

[Test]
public async Task LoginTest() {
//Arrange
LoginResponse loginResponse = new LoginResponse() {
//...
};

mockBrandFolderApi
.Setup(x => x.Login(It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(loginResponse);

//Act
string response = await subject.Login("username", "password");

//Assert
mockBrandFolderApi.Verify(x => x.Login(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
}
}

在生产代码中,使用容器注册和配置 IBrandFolderApi 抽象,应用任何需要的第 3 方依赖项

Startup.ConfigureServices

//...

ApiOptions apiOptions = Configuration.GetSection("ApiSettings").Get<ApiOptions>();
services.AddSingleton(apiOptions);

services.AddScoped<IBrandFolderApi>(sp => {
ApiOptions options = sp.GetService<ApiOptions>();
string url = options.Url;
return RestClient.For<IBrandFolderApi>(url);
});

其中ApiOptions用于存储设置

public class ApiOptions {
public string Url {get; set;}
//... any other API specific settings
}

可以在 appsetting.json 中定义

{
....

"ApiSettings": {
"Url": "https://brandfolder.companyname.io"
}
}

这样它们就不会在您的代码中被硬编码。

关于c# - 如何模拟/单元测试 HTTP 客户端 - restease,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68238847/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com