HttpClient ContinueWith Integration Test无需等待

时间:2018-02-13 10:01:16

标签: c# asp.net-core

背景

此代码在连接到GPS设备的小型RaspberryPi上运行。 GPS设备将GPS数据(纬度和经度)发送到RaspberryPi,RaspberryPi又将该数据发送到Amazon Web Services(云)上的服务。它适用于汽车远程信息处理。

问题

我希望编写一个集成测试,以确保当云的PostAsync失败时,通过记录器记录某些内容。我的代码如下所示:

private void PostMovementToCloud(TelematicsAppEvent telemetryEvent)
{
    var messageContent = new StringContent(JsonConvert.SerializeObject(telemetryEvent));
    messageContent.Headers.ContentType = MediaTypeJson;

    var result = _webClient.Value.PostAsync(GetEventEndPoint(telemetryEvent), messageContent, CancellationToken);
    result.ContinueWith(t => { ContinuationSteps(t, result); });
}

private void ContinuationSteps(Task<HttpResponseMessage> t, Task<HttpResponseMessage> result)
{
    if (t.IsFaulted || !result.IsCompletedSuccessfully)
    {
        // want to ensure this runs when the post to the cloud fails
        _logger.LogError(EventHttpClientError, () => $"Unable to post telemetry: {reason}");
    }
}

来自GPS的阅读速度快且频繁,我不想为每个.Wait()执行PostAsync,因为这会快速减慢/崩溃RaspberryPi。相反,我想在不等待响应的情况下发出请求。

但是,当我这样做时,如何在集成测试中测试ContinuationSteps中的代码确实在运行?

集成测试代码

[TestFixture]
public class MyTest
{
    private GpsSubscriberToJourneyEndPoint _gpsSubscriber;
    private readonly Mock<ITopicConsumer> _kafkaTopicConsumerMock = new Mock<ITopicConsumer>();

    [SetUp]
    public void RunBeforeAnyTests()
    {
        var gpsSubscriberToJourneyEndPointOptions = new GpsSubscriberToJourneyEndPointOptions
        {
            StartEndPoint = "journey-start-point-url",
            TimedEndPoint = "journey-mid-point-url",
            StopEndPoint = "journey-end-point-url"
        };

        var gpsSubscriberToJourneyEndPointOptionsMock = new Mock<IOptions<GpsSubscriberToJourneyEndPointOptions>>();
        var topicConsumerFactoryMock = new Mock<ITopicConsumerFactory>();
        var loggerMock = new Mock<ICharlesLogger>();
        var charlesLoggerFactoryMock = new Mock<ICharlesLoggerFactory>();

        gpsSubscriberToJourneyEndPointOptionsMock
            .SetupGet(o => o.Value)
            .Returns(gpsSubscriberToJourneyEndPointOptions);

        charlesLoggerFactoryMock
            .Setup(cl => cl.CreateLogger(It.IsAny<Type>()))
            .Returns(loggerMock.Object);

        topicConsumerFactoryMock
            .Setup(c => c.Create(It.IsAny<string>(), It.IsAny<IDictionary<string, string>>()))
            .Returns(_kafkaTopicConsumerMock.Object);

        _gpsSubscriber = new GpsSubscriberToJourneyEndPoint(
            gpsSubscriberToJourneyEndPointOptionsMock.Object,
            topicConsumerFactoryMock.Object, charlesLoggerFactoryMock.Object);
    }

    [Test]
    public void CheckFailingPostAsync()
    {
        // first and second reading have to be different in Lat and Lng to move the State machine to 'car moving state'
        const string firstGgaReading = "{\"Latitude\":53.639643,\"Longitude\":-1.782644,\"QualityIndicator\":2}";
        const string secondGgaReading = "{\"Latitude\":53.639399,\"Longitude\":-1.782834,\"QualityIndicator\":4}";
        _gpsSubscriber.Start(new CancellationToken());

        // Act
        // I'm raising the message received event here (message from gps device)
        _kafkaTopicConsumerMock
            .Raise(ktc => ktc.Message += null, new MessageEventArgs("Bam", firstGgaReading));

        _kafkaTopicConsumerMock
            .Raise(ktc => ktc.Message += null, new MessageEventArgs("Boom", secondGgaReading));
    }
}

1 个答案:

答案 0 :(得分:2)

我认为你无论如何都不应该创造一个即发即弃的任务。如果你创建了一个Task并让它活着,那么它可能会在你最不期望的时候回来咬你,你将很难找到实际发生的事情。

最好是PostMovementToCloud方法返回result Task实例。然后你可以在测试中等待它,你就可以正确地测试它。

hacky解决方案涉及人工等待(如Task.Delay),然后检查结果。如果您正在测试失败的情况,您可能会使任务快速失败,因此任何小的延迟都应该足够。但正如我所说,这是hacky而不是一个好的解决方案。