测试任务延迟

时间:2016-06-27 09:55:13

标签: c# moq autofixture

不确定如何实现这一目标。我试图对一个等待几分钟的方法进行单元测试,请看这里:

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:模拟上的预期调用至少一次,   但从未执行过:x =&gt;   x.Publish(It.Is(y =&gt; y.Imei == .imei))没有   设置已配置。没有进行任何调用。在   Moq.Mock.ThrowVerifyException(MethodCall expected,IEnumerable 1 setups, IEnumerable 1 actualCalls,Expression expression,Times times,   Int32 callCount)在Moq.Mock.VerifyCalls(拦截器   targetInterceptor,MethodCall expected,Expression expression,Times   时间)在Moq.Mock.Verify [T](模拟1 mock, Expression 1表达式,   Moq.Mock 1.Verify(Expression 1的时间,字符串failMessage)   表达)at   JustEat.PrivateAPN.Worker.UnitTests.TestRunners.JctRestartViaSmsAttemptTestFixture.RunJctTest_WhenRestartAttemptSmsIsSent_ShouldPublishJctRestartValidateMessageWithTheRightImei()   在

我知道我需要自定义/配置我的灯具才能进入回调但是我不知道该怎么做,任何帮助都会非常感激

3 个答案:

答案 0 :(得分:1)

你不应该在没有Task的情况下解雇await,以便在> 时完成,其他事情可能会依赖于结果。

所以我建议你改为:

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);
    }
}

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

替代

如果您无法更改方法的签名,那么您必须使用Thread.Sleep()来阻止当前线程,直到它完成。

因为您确实指定了Task.Delay我会假设您正在使用它来获取非阻止的好处。

如果您使用Thread.Sleep(),可能需要考虑在JctRestartViaSmsAttemptTestRunner中运行Task.Run(),这样只会阻止正在运行的线程。

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);
    }
}

测试

如果您有一个返回Task<>的方法,那么如果您不能拥有.Result测试方法签名,则必须使用async。如果您以串行方式运行所有测试,那么这应该不是问题。如果您不知道为什么.Result.Wait()读错了here

所以对于你的异步版本:

JctTest test = runner.ExecuteAsync().Result;

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

答案 1 :(得分:0)

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

所以,你可以尝试使用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)));
}

答案 2 :(得分:0)

我认为问题是允许测试完成。您在延迟启动后创建新任务但测试功能本身在此之前完成。

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