简体   繁体   English

Moq 测试 - 模拟服务调用始终返回 Null

[英]Moq Test - Mocked Service Call Always Returns Null

I am new to Moq and Mocks/Unit Testing in general.一般来说,我是 Moq 和 Mocks/Unit Testing 的新手。 After watching a video on Mocking which uses Moq , I thought I had enough understanding to start setting up a few simple tests for a project I am working on.在观看了使用 Moq 的 Mocking 视频后,我认为我有足够的理解开始为我正在处理的项目设置一些简单的测试。 However, no amount of fiddling with the code helps my first test pass.然而,对代码的任何摆弄都无法帮助我通过第一次测试。

Here is the code I have:这是我的代码:

Interface Being Tested正在测试的接口

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

Implementation of Interface Method GetData<T>接口方法 GetData<T> 的实现

        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
        }

Service Calling Implemented Interface GetData<T>服务调用实现接口 GetData<T>

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

My Test我的测试

        [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
            }
        }

Early on, I had issues because the project uses Autofac and Moq, but I resolved those specific issues.早些时候,我遇到了问题,因为该项目使用 Autofac 和 Moq,但我解决了这些具体问题。 However, I cannot seem to get past the null value coming back from the service call.但是,我似乎无法克服从服务调用返回的空值。 When running the project, the method returns data, just as expected, so I am flummoxed as to where the issue lies.运行项目时,该方法返回数据,正如预期的那样,所以我对问题所在感到困惑。 Reviewing various posts and the Moq Quickstart did not provide what I needed to solve this myself, so my hope is there is someone here who can tell me what I am doing wrong.查看各种帖子和 Moq Quickstart 并没有提供我自己解决这个问题所需的东西,所以我希望这里有人可以告诉我我做错了什么。 I apologize already as I am certain it is a newbie mistake.我已经道歉,因为我确定这是一个新手错误。

Addressing the implementation first.先说落实。 Avoid mixing async-await and blocking calls like Wait() and .Result which can result in deadlocks.避免混合异步等待和阻塞调用,如Wait().Result ,这可能导致死锁。

Reference Async/Await - Best Practices in Asynchronous Programming参考Async/Await - 异步编程的最佳实践

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
}

For the subject method under test, if nothing needs to be awaited then there is no need to make the function async, just return the Task对于被测主题方法,如果不需要等待,则无需使函数异步,只需返回Task

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

Now for the test, since already using tasks, then the test should be async and the subject awaited as well.现在进行测试,因为已经在使用任务,那么测试应该是异步的,并且主题也在等待。

[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);
        // ...
    }
}

Assuming, based on what was shown it is uncertain what the active URL would have been, it could be ignored in the test using It.IsAny<string>() .假设根据显示的内容不确定活动 URL 是什么,可以在测试中使用It.IsAny<string>()将其忽略。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM