最小起订量测试 - 模拟服务调用总是 Returns Null

Moq Test - Mocked Service Call Always Returns Null

我是 Moq 新手,Mocks/Unit 一般测试。看完 a video on Mocking which uses Moq 之后,我认为我已经有了足够的了解,可以开始为我正在从事的项目设置一些简单的测试。然而,再多的代码修改也无法帮助我通过第一次测试。

这是我的代码:

接口正在测试

    public interface IDataInterface
    {
        Task<IList<T>> GetData<T>(string url);
        // Other stuff
    }

接口方法 GetData 的实现

        public async Task<IList<T>> GetData<T>(string url)
        {
            try
            {
                var json = await client.GetAsync(url).Result.Content.ReadAsStringAsync();
                var data = (JObject)JsonConvert.DeserializeObject(json);
                JArray arr = (JArray)data["resource"];
                return arr.ToObject<IList<T>>();
            }
            catch (InvalidCastException icEx)
            {
                throw new InvalidCastException("An error occurred when retrieving the data", icEx);
            }
            // Other catches
        }

服务调用实现接口 GetData

        public async Task<IList<MyObj>> GetActiveObjs()
        {
            var data = await _myImplementedInterface.GetData<MyObj>(ActiveUrl);
            return data;
        }

我的测试

        [Fact]
        public async void MyImplementedInterface_GetActive_ReturnsDataOrEmptyList()
        {
            var _activeUrl = "http://some.url";
            using (var mock = AutoMock.GetLoose())
            {
                mock.Mock<IDataInterface>()
                    .Setup(x => x.GetData<MyObj>(_activeUrl))
                    .Returns(Task.FromResult(_SomeStaticDataRepresentation)));

                var svc = mock.Create<MyService>();

                var expected = _SomeStaticDataRepresentation;
                var actual = await svc.GetActiveObjs();

                Assert.True(actual != null);
                // Other assertions that never matter because of the null value of _actual_ variable
            }
        }

早些时候,我遇到了一些问题,因为该项目使用了 Autofac 和 Moq,但我解决了这些具体问题。但是,我似乎无法通过从服务调用返回的空值。当运行项目时,方法returns数据,果然不出所料,所以我很困惑问题出在哪里。查看各种帖子和 Moq Quickstart 没有提供我自己解决这个问题所需的东西,所以我希望这里有人可以告诉我我做错了什么。我已经道歉,因为我确定这是一个新手错误。

首先解决实施问题。避免混合异步等待和阻塞调用,例如 Wait().Result,这可能会导致死锁。

引用Async/Await - Best Practices in Asynchronous Programming

public async Task<IList<T>> GetData<T>(string url) {
    try {
        var response = await client.GetAsync(url);
        var json = await response.Content.ReadAsStringAsync();
        var data = (JObject)JsonConvert.DeserializeObject(json);
        JArray arr = (JArray)data["resource"];
        return arr.ToObject<IList<T>>();
    } catch (InvalidCastException icEx) {
        throw new InvalidCastException("An error occurred when retrieving the data", icEx);
    }
    // Other catches
}

对于被测的主题方法,如果不需要等待那么函数就不需要异步,只需return Task

public Task<IList<MyObj>> GetActiveObjs() {
    return _myImplementedInterface.GetData<MyObj>(ActiveUrl);
}

现在进行测试,既然已经使用了任务,那么测试应该是异步的,并且主题也是等待的。

[Fact]
public async Task MyImplementedInterface_GetActive_ReturnsDataOrEmptyList() {
    using (var mock = AutoMock.GetLoose()) {
        //Arrange
        IList<MyObj> expected = _SomeStaticDataRepresentation;
        mock.Mock<IDataInterface>()
            .Setup(x => x.GetData<MyObj>(It.IsAny<string>()))
            .ReturnAsync(expected);

        var svc = mock.Create<MyService>();

        //Act
        var actual = await svc.GetActiveObjs();

        //Assert
        Assert.True(actual != null);
        Assert.True(actual == expected);
        // ...
    }
}

假设,根据显示的内容,不确定活动的 URL 是什么,可以在使用 It.IsAny<string>() 的测试中忽略它。