测试任务延迟

Testing task delay

不确定如何实现。我正在尝试对等待几分钟的方法进行单元测试,请参阅此处:

internal class JctRestartViaSmsAttemptTestRunner : ITestRunner<JctRestartViaSmsAttempt>
{
    private readonly IMayWantMonitoring _queues;
    private readonly IAppSettings _appSettings;

    public JctRestartViaSmsAttemptTestRunner(IMayWantMonitoring queues, IAppSettings appSettings)
    {
        _queues = queues;
        _appSettings = appSettings;
    }

    public JctTest Execute(JctRestartViaSmsAttempt jctMessageType)
    {
        // after five minutes, publish an event to check if the JCT logged in
        var jctLoggedInTimeOut = TimeSpan.FromMinutes(double.Parse(_appSettings["JctLogInTimeOut"]));
        var message = new JctRestartViaSmsValidate(jctMessageType.Imei);

        Task.Delay(jctLoggedInTimeOut)
            .ContinueWith(x => _queues.Publish(message));

        // reset test values
        return new JctTest("6", jctMessageType.Imei, null, null, null);
    }
}

这是我的测试方法,但我无法模拟任务延迟。

    [Test]
    public void TaskTest()
    {
        // arrange
        var imei = _fixture.Create<string>();
        _appSettings.Setup(c => c["JctLogInTimeOut"]).Returns("5");
        var message = _fixture.Build<JctRestartViaSmsAttempt>()
            .With(x => x.Imei, imei)
            .Create();

        var sut = _fixture.Create<JctRestartViaSmsAttemptTestRunner>();

        // act
        sut.Execute(message);

        // assert
        _queues.Verify(x => x.Publish(It.Is<JctRestartViaSmsValidate>(y => y.Imei == imei)));
    }

这是引发的错误:

Moq.MockException : Expected invocation on the mock at least once, but was never performed: x => x.Publish(It.Is(y => y.Imei == .imei)) No setups configured. No invocations performed. at Moq.Mock.ThrowVerifyException(MethodCall expected, IEnumerable1 setups, IEnumerable1 actualCalls, Expression expression, Times times, Int32 callCount) at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times) at Moq.Mock.Verify[T](Mock1 mock, Expression1 expression, Times times, String failMessage) at Moq.Mock1.Verify(Expression1 expression) at JustEat.PrivateAPN.Worker.UnitTests.TestRunners.JctRestartViaSmsAttemptTestFixture.RunJctTest_WhenRestartAttemptSmsIsSent_ShouldPublishJctRestartValidateMessageWithTheRightImei() in

我知道我需要 customize/configure 我的 fixture 才能进入回调,但我不确定该怎么做,任何帮助将不胜感激

execute 方法不是真正可测试的,因为它启动了一个延迟任务,但您无法从外部管理该任务。

因此,您可以尝试使用 Thread.Sleep,这不太好但应该可以:

[Test]
public void TaskTest()
{
    // arrange
    var imei = _fixture.Create<string>();
    _appSettings.Setup(c => c["JctLogInTimeOut"]).Returns("1");
    var message = _fixture.Build<JctRestartViaSmsAttempt>()
        .With(x => x.Imei, imei)
        .Create();

    var sut = _fixture.Create<JctRestartViaSmsAttemptTestRunner>();

    // act
    sut.Execute(message);

    // verify the task was not executed yet
    // ....

    // let the test thread sleep and give the task some time to run
    Thread.Sleep(2000);

    // assert
    _queues.Verify(x => x.Publish(It.Is<JctRestartViaSmsValidate>(y => y.Imei == imei)));
}

我认为问题是允许测试完成。您在延迟开始后创建了一个新任务,但测试功能本身在此之前完成。

我对测试框架不是很熟悉。但我希望你想在任务结束时使用 .Wait() 。这将创建一个阻塞任务,您的主测试线程将被阻塞,直到延迟完成。

你不应该在 await 未完成 的情况下关闭 Task,当 你的测试和其他事情可能取决于结果.

所以我建议你改成:

internal class JctRestartViaSmsAttemptTestRunner : ITestRunner<JctRestartViaSmsAttempt>
{
    private readonly IMayWantMonitoring _queues;
    private readonly IAppSettings _appSettings;

    public JctRestartViaSmsAttemptTestRunner(IMayWantMonitoring queues, IAppSettings appSettings)
    {
        _queues = queues;
        _appSettings = appSettings;
    }

    public async Task<JctTest> ExecuteAsync(JctRestartViaSmsAttempt jctMessageType)
    {
        // after five minutes, publish an event to check if the JCT logged in
        var jctLoggedInTimeOut = TimeSpan.FromMinutes(double.Parse(_appSettings["JctLogInTimeOut"]));
        var message = new JctRestartViaSmsValidate(jctMessageType.Imei);

        // this will now delay in a non blocking fashion.
        await Task.Delay(jctLoggedInTimeOut);

        _queues.Publish(message);

        // reset test values
        return new JctTest("6", jctMessageType.Imei, null, null, null);
    }
}

awaiting 的另一个原因是你有这个 _queues 如果你打算稍后从中读取,你永远不能保证内容,因为线程池中可能仍然有一个线程处理 Task.Delay.

备选

如果您不能更改方法的签名,那么您将不得不使用 Thread.Sleep(),它将阻塞当前线程,直到它完成。

因为您确实指定了 Task.Delay 我假设您使用它是为了获得非阻塞的好处。

如果您要使用 Thread.Sleep(),您可能需要考虑 运行在 Task.Run() 中设置 JctRestartViaSmsAttemptTestRunner,这样只会阻塞它所在的线程 运行继续。

internal class JctRestartViaSmsAttemptTestRunner : ITestRunner<JctRestartViaSmsAttempt>
{
    private readonly IMayWantMonitoring _queues;
    private readonly IAppSettings _appSettings;

    public JctRestartViaSmsAttemptTestRunner(IMayWantMonitoring queues, IAppSettings appSettings)
    {
        _queues = queues;
        _appSettings = appSettings;
    }

    public JctTest Execute(JctRestartViaSmsAttempt jctMessageType)
    {
        // after five minutes, publish an event to check if the JCT logged in
        var jctLoggedInTimeOut = TimeSpan.FromMinutes(double.Parse(_appSettings["JctLogInTimeOut"]));
        var message = new JctRestartViaSmsValidate(jctMessageType.Imei);

        Thread.Wait(jctLoggedInTimeOut.Milliseconds);

        _queues.Publish(message);

        // reset test values
        return new JctTest("6", jctMessageType.Imei, null, null, null);
    }
}

测试

如果您有执行 return Task<> 的方法,那么如果您没有 async 测试方法签名,则必须使用 .Result。如果您 运行 连续进行所有测试,这应该不是问题。如果您不知道为什么 .Result.Wait() 不好,请阅读 here

所以对于你的异步版本:

JctTest test = runner.ExecuteAsync().Result;

并且您的非异步版本保持不变。