Я создаю REST API, который будет вызывать (получать) n других REST API, объединять их ответы и затем отправлять их клиенту.
Я очень обеспокоен тем, что мой асинхронный метод, который вызывает другие услуги не так. У меня уже был тест, использующий moq, чтобы высмеивать ответы API. В течение нескольких дней, пока я тестировал приложение и выполнял тесты, оно работало, а затем на сервере сборки оно ломалось (в следующей попытке оно не ломалось). Очевидно, я знал, что это проблема с моими асинхронными вызовами.
Моя цель - создать надежный метод тестирования, имитирующий поведение и задержку многих вызовов API.
Рабочий код:
private async Task<List<T>> GetInformationFromExternalSource<T>(List<string> urls) where T : BaseDTO
{
var response = new List<T>();
var tasks = new List<Task<List<T>>>();
urls.ForEach(url =>
{
tasks.Add(Task.Run(() =>
{
string infoJson = RequestDataFromProvider(url).Result;
List<T> result = JsonConvert.DeserializeObject<List<T>>(infoJson);
return result;
}));
});
await Task.WhenAll(tasks.ToArray());
tasks.ForEach(t => response.AddRange(t.Result));
return response;
}
private virtual Task<string> RequestDataFromProvider(string url)
{
HttpClient client = _clientProvider.getClient();
var result = client.GetAsync(url).Result;
return result.Content.ReadAsStringAsync();
}
Тестовый код:
[Theory]
[InlineData(3, 2, 5, 7, 17)]
[InlineData(30, 20, 50, 70, 170)]
[InlineData(1, 0, 0, 0, 1)]
[InlineData(43, 1, 0, 0, 44)]
[InlineData(1, 1, 1, 1, 4)]
[InlineData(300, 100, 150, 50, 600)]
[InlineData(0, 0, 0, 0, 0)]
public async Task GetJsons_WhenHasResponseInManyCalls_ShouldConcatenate(int s1, int s2, int s3, int s4, int expCount)
{
//Arrange
var fakeHttpMessageHandler = new Mock<FakeHttpMessageHandler>(); //here I also tried to use
//a SleepyHttpMessageHandler, but I threw a lot of aggregate exceptions because of my Thread.Sleep
var client = new HttpClient(fakeHttpMessageHandler.Object);
var providerMock = new Mock<IHttpProvider>();
providerMock.Setup(f => f.getClient()).Returns(client);
var myService = new MyService(providerMock.Object);
//Arrange
_fakeHttpMessageHandler.SetupSequence(f => f.Send(It.IsAny<HttpRequestMessage>()))
.Returns(httpResponseWithContent(createDummyDto(s1)))
.Returns(httpResponseWithContent(createDummyDto(s2)))
.Returns(httpResponseWithContent(createDummyDto(s3)))
.Returns(httpResponseWithContent(createDummyDto(s4))); //I mocked the DB to have only 4 urls
//Act
List<DummyDto> result = await myService.getRequest<DummyDto>(); //getAction will get the urls in database and then return GetInformationFromExternalSource<DummyDto>(urls)
//Assert
result.Should().HaveCount(expCount);
}
private static HttpResponseMessage httpResponseWithContent(List<DummyDto> content1)
{
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(JsonConvert.SerializeObject(content1))
};
}
FakeHttpMessageHandler:
public class FakeHttpMessageHandler : HttpMessageHandler
{
public virtual HttpResponseMessage Send(HttpRequestMessage request)
{
throw new NotImplementedException("Now we can setup this method with our mocking framework");
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
//Thread.Sleep(100);
return Task.FromResult(Send(request));
}
}
Я хочу подождать случайное время, пока ответ не будет отправлен потокам.
Я пробовал setupSequence
, callBack
, но все они привели к большему количеству неудачных тестов, чем к прохождению.