使用 Moq 模拟单元测试的异步方法 [英] Using Moq to mock an asynchronous method for a unit test

查看:35
本文介绍了使用 Moq 模拟单元测试的异步方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在测试用于进行 Web API 调用的服务的方法.如果我还在本地运行 Web 服务(位于解决方案中的另一个项目中),则使用普通的 HttpClient 可以很好地进行单元测试.

I am testing a method for a service that makes a Web API call. Using a normal HttpClient works fine for unit tests if I also run the web service (located in another project in the solution) locally.

但是,当我签入更改时,构建服务器将无法访问 Web 服务,因此测试将失败.

However when I check in my changes the build server won't have access to the web service so the tests will fail.

我通过创建一个 IHttpClient 接口并实现我在应用程序中使用的版本,为我的单元测试设计了一种方法.对于单元测试,我使用模拟的异步 post 方法制作了一个完整的模拟版本.这是我遇到问题的地方.我想为这个特定的测试返回一个 OK HttpStatusResult.对于另一个类似的测试,我将返回一个糟糕的结果.

I've devised a way around this for my unit tests by creating an IHttpClient interface and implementing a version that I use in my application. For unit tests, I make a mocked version complete with a mocked asynchronous post method. Here's where I have run into problems. I want to return an OK HttpStatusResult for this particular test. For another similar test I will be returning a bad result.

测试将运行但永远不会完成.它挂在等待.我是异步编程、委托和 Moq 本身的新手,我一直在搜索 SO 和 google 一段时间以学习新事物,但我似乎仍然无法解决这个问题.

The test will run but will never complete. It hangs at the await. I am new to asynchronous programming, delegates, and Moq itself and I've been searching SO and google for a while learning new things but I still can't seem to get past this problem.

这是我要测试的方法:

public async Task<bool> QueueNotificationAsync(IHttpClient client, Email email)
{
    // do stuff
    try
    {
        // The test hangs here, never returning
        HttpResponseMessage response = await client.PostAsync(uri, content);

        // more logic here
    }
    // more stuff
}

这是我的单元测试方法:

Here's my unit test method:

[TestMethod]
public async Task QueueNotificationAsync_Completes_With_ValidEmail()
{
    Email email = new Email()
    {
        FromAddress = "bob@example.com",
        ToAddress = "bill@example.com",
        CCAddress = "brian@example.com",
        BCCAddress = "ben@example.com",
        Subject = "Hello",
        Body = "Hello World."
    };
    var mockClient = new Mock<IHttpClient>();
    mockClient.Setup(c => c.PostAsync(
        It.IsAny<Uri>(),
        It.IsAny<HttpContent>()
        )).Returns(() => new Task<HttpResponseMessage>(() => new HttpResponseMessage(System.Net.HttpStatusCode.OK)));

    bool result = await _notificationRequestService.QueueNotificationAsync(mockClient.Object, email);

    Assert.IsTrue(result, "Queue failed.");
}

我做错了什么?

感谢您的帮助.

推荐答案

您正在创建一项任务,但从未启动它,因此它永远不会完成.但是,不要只是开始任务 - 而是改为使用 Task.FromResult 这会给你一个已经完成的任务:

You're creating a task but never starting it, so it's never completing. However, don't just start the task - instead, change to using Task.FromResult<TResult> which will give you a task which has already completed:

...
.Returns(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)));

请注意,您不会以这种方式测试实际的异步 - 如果您想这样做,您需要做更多的工作来创建一个您可以控制的 Task以更细粒度的方式......但那是另一天的事情.

Note that you won't be testing the actual asynchrony this way - if you want to do that, you need to do a bit more work to create a Task<T> that you can control in a more fine-grained manner... but that's something for another day.

您可能还想考虑为 IHttpClient 使用一个假的,而不是模拟一切——这实际上取决于您需要它的频率.

You might also want to consider using a fake for IHttpClient rather than mocking everything - it really depends on how often you need it.

这篇关于使用 Moq 模拟单元测试的异步方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆