C#模拟自定义接口

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

我有一个我正在测试的函数,叫做CreateMessageHistory,它有两个依赖项,IhttpClientFactory和另一个接口,我可以模拟httpclient,但我不能模拟接口

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