Incorporeal Logic
Incorporeal Logic

Reputation: 280

Moq Test - Mocked Service Call Always Returns Null

I am new to Moq and Mocks/Unit Testing in general. 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. 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>

        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>

        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. 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. I apologize already as I am certain it is a newbie mistake.

Upvotes: 1

Views: 1324

Answers (1)

Nkosi
Nkosi

Reputation: 246998

Addressing the implementation first. Avoid mixing async-await and blocking calls like Wait() and .Result which can result in deadlocks.

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

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

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>().

Upvotes: 3

Related Questions