C#模拟自定义接口
我有一个我正在测试的函数,叫做CreateMessageHistory,它有两个依赖项,IhttpClientFactory和另一个接口,我可以模拟httpclient,但我不能模拟接口C#模拟自定义接口,c#,asp.net-core,moq,xunit,C#,Asp.net Core,Moq,Xunit,我有一个我正在测试的函数,叫做CreateMessageHistory,它有两个依赖项,IhttpClientFactory和另一个接口,我可以模拟httpclient,但我不能模拟接口 public class MessageHistoryService : IMessageHistoryService { private const string API_MESSAGING_DB = "messages"; private const string API_GET_MESSA
public class MessageHistoryService : IMessageHistoryService
{
private const string API_MESSAGING_DB = "messages";
private const string API_GET_MESSAGE_HISTORY_DESIGN = "_design/upn-timesent";
private const string API_GET_MESSAGE_HISTORY_DESIGN_VIEW = "_view/upn-timesent-query";
private readonly ICouchDbClients couchDbClient;
private readonly IHttpClientFactory clientFactory;
public MessageHistoryService(
IHttpClientFactory clientFactory,
ICouchDbClients couchDbClient)
{
this.couchDbClient = couchDbClient ??
throw new ArgumentNullException(nameof(couchDbClient));
this.clientFactory = clientFactory ??
throw new ArgumentNullException(nameof(clientFactory));
}
public async Task CreateMessageHistory(Message message)
{
var client = this.clientFactory.CreateClient(NamedHttpClients.COUCHDB);
var formatter = new JsonMediaTypeFormatter();
formatter.SerializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
Guid id = Guid.NewGuid();
var response = await this.couchDbClient.AuthenticatedQuery(async () => {
return await client.PutAsync($"{API_MESSAGING_DB}/{id.ToString()}", message, formatter);
}, NamedHttpClients.COUCHDB, client);
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(await response.Content.ReadAsStringAsync());
}
}
}
我似乎对这个界面有问题
public interface ICouchDbClients
{
Task<HttpResponseMessage> AuthenticatedQuery(Func<Task<HttpResponseMessage>> query, string name, HttpClient client);
}
公共接口ICouchDBClient
{
任务AuthenticatedQuery(Func查询,字符串名称,HttpClient);
}
以下是该接口的实现:
public class CouchDbClients : ICouchDbClients
{
private readonly Dictionary<string, string> authSessions;
private readonly Dictionary<string, Credentials> couchDbCredentials;
private readonly IOptions<Clients> clients;
private readonly string AuthCouchDbCookieKeyName = "AuthSession";
public CouchDbClients(IOptions<Clients> clients)
{
this.clients = clients ??
throw new ArgumentNullException(nameof(couchDbCredentials));
this.couchDbCredentials = new Dictionary<string, Credentials>();
this.couchDbCredentials.Add(NamedHttpClients.COUCHDB, this.clients.Value.Work);
this.authSessions = new Dictionary<string, string>();
}
public async Task<HttpResponseMessage> AuthenticatedQuery(Func<Task<HttpResponseMessage>> query, string name, HttpClient client)
{
int counter = 0;
var response = new HttpResponseMessage();
do
{
Authenticate(name, client);
response = await query();
if (response.IsSuccessStatusCode)
{
break;
}
else if (response.StatusCode == HttpStatusCode.Unauthorized)
{
this.authSessions[name] = GenerateCookie(client, name);
}
else
{
throw new HttpRequestException(await response.Content.ReadAsStringAsync());
}
counter++;
} while (counter < 3);
return response;
}
private void Authenticate(string name, HttpClient client)
{
CookieContainer container = new CookieContainer();
var session = this.authSessions.ContainsKey(name);
if (!session)
{
var newCookie = GenerateCookie(client, name);
authSessions.Add(name, newCookie);
}
container.Add(
client.BaseAddress,
new Cookie(AuthCouchDbCookieKeyName, this.authSessions[name])
);
}
private string GenerateCookie(HttpClient client, string name)
{
string authPayload = JsonConvert.SerializeObject(this.couchDbCredentials[name]);
var authResult = client.PostAsync(
"_session",
new StringContent(authPayload, Encoding.UTF8, "application/json")
).Result;
if (authResult.IsSuccessStatusCode)
{
var responseHeaders = authResult.Headers.ToList();
string plainResponseLoad = authResult.Content.ReadAsStringAsync().Result;
var authCookie = responseHeaders
.Where(r => r.Key == "Set-Cookie")
.Select(r => r.Value.ElementAt(0)).FirstOrDefault();
if (authCookie != null)
{
int cookieValueStart = authCookie.IndexOf("=") + 1;
int cookieValueEnd = authCookie.IndexOf(";");
int cookieLength = cookieValueEnd - cookieValueStart;
string authCookieValue = authCookie.Substring(cookieValueStart, cookieLength);
return authCookieValue;
}
else
{
throw new Exception("There is auth cookie header in the response from the CouchDB API");
}
}
else
{
throw new HttpRequestException(string.Concat("Authentication failure: ", authResult.ReasonPhrase));
}
}
}
公共类CouchDBClient:ICouchDBClient
{
专用只读字典授权会话;
私人只读词典;
私有只读客户端;
私有只读字符串AuthCouchDbCookieKeyName=“AuthSession”;
公共CouchDBClient(IOptions客户端)
{
this.clients=客户机??
抛出新ArgumentNullException(nameof(CouchdbRedentials));
this.couchDbCredentials=新字典();
this.couchDbCredentials.Add(NamedHttpClients.COUCHDB,this.clients.Value.Work);
this.authSessions=新字典();
}
公共异步任务AuthenticatedQuery(Func查询、字符串名称、HttpClient客户端)
{
int计数器=0;
var response=新的HttpResponseMessage();
做
{
认证(名称、客户);
response=等待查询();
if(响应。IsSuccessStatusCode)
{
打破
}
否则如果(response.StatusCode==HttpStatusCode.Unauthorized)
{
this.authSessions[name]=GenerateCookie(客户端,名称);
}
其他的
{
抛出新的HttpRequestException(wait-response.Content.ReadAsStringAsync());
}
计数器++;
}而(计数器<3);
返回响应;
}
私有void身份验证(字符串名称,HttpClient)
{
CookieContainer容器=新CookieContainer();
var session=this.authSessions.ContainsKey(名称);
如果(!会话)
{
var newCookie=GenerateCookie(客户,名称);
authSessions.Add(名称,newCookie);
}
容器。添加(
client.BaseAddress,
新Cookie(AuthCouchDbCookieKeyName,this.authSessions[name])
);
}
私有字符串GenerateCookie(HttpClient客户端,字符串名称)
{
字符串authPayload=JsonConvert.SerializeObject(this.couchDbCredentials[name]);
var authResult=client.PostAsync(
“_会议”,
新的StringContent(authPayload,Encoding.UTF8,“application/json”)
).结果;
if(authResult.IsSuccessStatusCode)
{
var responseHeaders=authResult.Headers.ToList();
字符串plainResponseLoad=authResult.Content.ReadAsStringAsync().Result;
var authCookie=responseHeaders
.Where(r=>r.Key==“设置Cookie”)
.Select(r=>r.Value.ElementAt(0)).FirstOrDefault();
if(authCookie!=null)
{
int-cookieValueStart=authCookie.IndexOf(“=”)+1;
int-cookieValueEnd=authCookie.IndexOf(“;”);
int cookieLength=cookieValueEnd-cookieValueStart;
字符串authCookieValue=authCookie.Substring(cookieValueStart,cookieLength);
返回authCookieValue;
}
其他的
{
抛出新异常(“CouchDB API的响应中存在auth cookie头”);
}
}
其他的
{
抛出新的HttpRequestException(string.Concat(“身份验证失败:”,authResult.reasonPhase));
}
}
}
下面是我的单元测试:
[Fact]
public async Task Should_NotThrowHttpRequestException_When_AMessageHistoryIsCreated()
{
var recipients = MockMessage.GetRecipients(279, 1, 2, 3);
var message = MockMessage.GetMessage(recipients);
mockStateFixture
.MockMessageHistoryService
.Setup(service => service.CreateMessageHistory(message));
// ARRANGE
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.Created
})
.Verifiable();
// create the mock client
// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
BaseAddress = new Uri("http://10.179.236.207:5984/"),
};
mockStateFixture.MockIHttpClientFactory.Setup(x => x.CreateClient(NamedHttpClients.COUCHDB))
.Returns(httpClient);
//var httpResponseMessage = new Mock<Task<HttpResponseMessage>>();
//httpResponseMessage.Setup
var httpResponseMessage = new HttpResponseMessage
{
StatusCode = HttpStatusCode.Created
};
//httpResponseMessage.Setup(x => x.StatusCode = HttpStatusCode.Created);
//mockStateFixture.MockCouchDbClient.Setup(x => x.AuthenticatedQuery(
// async () =>
// {
// return await httpResponseMessage;
// },
// NamedHttpClients.COUCHDB,
// httpClient))
// .Returns(It.IsAny<Task<HttpResponseMessage>>);
var messageHistoryService = new MessageHistoryService(
mockStateFixture.MockIHttpClientFactory.Object, mockStateFixture.MockCouchDbClient.Object);
var task = messageHistoryService.CreateMessageHistory(message);
var type = task.GetType();
Assert.True(type.GetGenericArguments()[0].Name == "VoidTaskResult");
Assert.True(type.BaseType == typeof(Task));
await task;
}
[事实]
创建消息历史记录()时,公共异步任务不应通过whttprequesteexception
{
var recipients=MockMessage.GetRecipients(279,1,2,3);
var message=MockMessage.GetMessage(收件人);
模拟固定装置
.MockMessageHistoryService
.Setup(service=>service.CreateMessageHistory(message));
//安排
var handlerMock=new Mock(MockBehavior.Strict);
handlerMock
.Protected()
.设置(
“SendAsync”,
ItExpr.IsAny(),
ItExpr.IsAny())
.ReturnsAsync(新的HttpResponseMessage()
{
StatusCode=HttpStatusCode.Created
})
.可验证();
//创建模拟客户端
//在此处使用带模拟处理程序的真实http客户端
var httpClient=新的httpClient(handlerMock.Object)
{
BaseAddress=新Uri(“http://10.179.236.207:5984/"),
};
mockStateFixture.MockIHttpClientFactory.Setup(x=>x.CreateClient(NamedHttpClients.COUCHDB))
.返回(httpClient);
//var httpResponseMessage=new Mock();
//httpResponseMessage.Setup
var httpResponseMessage=新httpResponseMessage
{
StatusCode=HttpStatusCode.Created
};
//httpResponseMessage.Setup(x=>x.StatusCode=HttpStatusCode.Created);
//mockStateFixture.MockCouchDbClient.Setup(x=>x.AuthenticatedQuery(
//异步()=>
// {
//返回等待httpResponseMessage;
// },
//命名为dhttpclients.COUCHDB,
//httpClient)
//.返回(It.IsAny);
var messageHistoryService=newmessagehistoryservice(
mockStateFixture.MockIHttpClientFactory.Object、mockStateFixture.MockCouchDbClient.Object);
var task=messageHistoryService.CreateMessageHistory(消息);
var type=task.GetType();
Assert.True(type.GetGen