我正在测试此函数,称为CreateMessageHistory,它具有2个依赖项,即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);
}
这是该接口的实现:
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));
}
}
}
这是我的单元测试:
[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;
}
我该如何模拟ICouchDbClient,我甚至必须这么做?
这是我得到的错误:
测试名称:
Data.Tests.MessageHistoryServiceTests + CreateMessageHistory.Should_NotThrowHttpRequestException_When_AMessageHistoryIsCreated 测试 全名:Data.Tests.MessageHistoryServiceTests + CreateMessageHistory.Should_NotThrowHttpRequestException_When_AMessageHistoryIsCreated 测试源:C:\ Data.Tests \ MessageHistoryServiceTests.cs:第36行 测试结果:测试持续时间失败:0:00:00.393
结果StackTrace:
在Data.API.MessageHistoryService.CreateMessageHistory(Message 消息)在C:\ Data \ API \ MessageHistoryService.cs:第51行 Data.Tests.MessageHistoryServiceTests.CreateMessageHistory.Should_NotThrowHttpRequestException_When_AMessageHistoryIsCreated() 在C:\ Data.Tests \ MessageHistoryServiceTests.cs:line 100中 ---从引发异常的先前位置结束的堆栈跟踪---结果消息:System.NullReferenceException:Object 引用未设置为对象的实例。