【问题标题】:Testing task delay测试任务延迟
【发布时间】:2016-06-27 09:55:13
【问题描述】:

不知道如何实现这一点。我正在尝试对等待几分钟的方法进行单元测试,请参见此处:

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 => x.Publish(It.Is(y => y.Imei == .imei)) 否 配置的设置。未执行任何调用。在 Moq.Mock.ThrowVerifyException(MethodCall 预期,IEnumerable1 setups, IEnumerable1 actualCalls,Expression 表达式,Times 次, Int32 callCount) 在 Moq.Mock.VerifyCalls(Interceptor targetInterceptor、MethodCall 预期、Expression 表达式、Times 次)在 Moq.Mock.Verify[T](Mock1 mock, Expression1 表达式, 次次,字符串 failMessage) 在 Moq.Mock1.Verify(Expression1 表达式)在 JustEat.PrivateAPN.Worker.UnitTests.TestRunners.JctRestartViaSmsAttemptTestFixture.RunJctTest_WhenRestartAttemptSmsIsSent_ShouldPublishJctRestartValidateMessageWithTheRightImei() 在

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

【问题讨论】:

  • 我要验证_queues.Publish(message)的调用
  • JctRestartViaSmsValidate != JctRestartViaSmsAttempt
  • var message = new JctRestartViaSmsValidate(jctMessageType.Imei); 是放在_queues 中的内容,_queues.Verify(x =&gt; x.Publish(It.Is&lt;JctRestartViaSmsAttempt&gt;(y =&gt; y.Imei == imei))); 是您验证要进入队列的内容?即使这不能解决您的问题,在我看来它仍然是错误的
  • 没错,我只是更新问题。错误还是一样。
  • Task.Delay 在这里使用是完全错误的。您可能想尝试Thread.Sleep。如果你想要非阻塞版本,你需要让你的函数asyncawaitTask.Delay

标签: c# moq autofixture


【解决方案1】:

您不应该在没有 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);
    }
}

测试

如果您有一个确实返回Task&lt;&gt; 的方法,那么如果您不能拥有async 测试方法签名,那么您将不得不使用.Result。如果您串行运行所有测试,这应该不是问题。如果你不知道为什么 .Result.Wait() 不好读 here

所以对于你的异步版本:

JctTest test = runner.ExecuteAsync().Result;

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

【讨论】:

  • 使用await时,不需要ContinueWith。
  • 是的,刚刚改了
  • 如果Execute 不是接口的一部分并且可以更改方法签名,则此解决方案是可行的方法。请注意,您必须在单元测试中调用Execute(...).Wait()
  • 我会把替代品放进去
  • 考虑按照 .Net 命名约定将 Execute 重命名为 ExecuteAsync
【解决方案2】:

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

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

【讨论】:

  • 不,永远不要使用.Wait()。请改用await Task.Delay
  • 您可能无法在此测试框架中使用 await。如果你可以使用 await 那当然会更好。否则,据我所知,阻塞将是不可避免的。该函数没有用 async 修饰。
  • 啊,抱歉,不清楚你想打电话给.Wait()。你必须在测试中,这很好,因为你不应该遇到死锁的问题。但是,他当前的方法签名没有公开Task&lt;&gt; 的返回类型,所以你不能.Wait()
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 2018-10-21
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 2017-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-19
    相关资源
    最近更新 更多