-3

I've done a lot of heavy modification to some async code, and I've been tinkering with it long enough that I am in need of a second set of eyes.

In a unit test, I'm calling my async code, and then validating the values it should return. The object that ultimately performs the async operation is mocked.

The Unit Test:

    public void GetCharactersAsync_ById_Test()
    {
        var charDictionary = TestHelper.GetCharactersIdDictionary(1);
        var mockReq = new Mock<IRequester>();
        mockReq.Setup(x => x.CreateGetRequestAsync<Dictionary<long, Character>>
                            (It.IsAny<string>())).ReturnsAsync(charDictionary);
        var char1 = TestHelper.GetCharacter();
        var api = GameObject.GetInstance(mockReq.Object);

        var character = api.GetCharacterAsync(TestHelper.GetRegion(), (int)char.Id);

        Assert.AreEqual(character.Result.Name, char1.Name);
    }

GetCharacterAsync:

    public async Task<Character> GetCharacterAsync(Region region, int charId)
    {
        var res = await requester.CreateGetRequestAsync<Task<Dictionary<long, Character>>>
                       (bld.BuildUrlForEndpoint("GetCharacter",
                       region, charId.ToString()));
        var obj = res.Result.Values.FirstOrDefault();
        return obj;
    }

requester.CreateGetRequestAsync (though this should be mocked and not matter):

    public async Task<T> CreateGetRequestAsync<T>(string urlString)
    {
        return await this.GetResultAsyncDeserialized<T>(new HttpRequestMessage(HttpMethod.Get, urlString));
    }

and finally, GetResultAsyncDeserialized (though this is mocked and shouldn't matter):

    protected async Task<T> GetResultAsyncDeserialized<T>(HttpRequestMessage request)

    {
        var result = string.Empty;
        using (var response = await httpClient.GetAsync(request.RequestUri))
        {
            if (!response.IsSuccessStatusCode)
            {
                HandleRequestFailure(response.StatusCode);
            }
            using (var content = response.Content)
            {
                result = await content.ReadAsStringAsync();
            }
        }
        return JsonConvert.DeserializeObject<T>(result);
    }

I have next to zero experience with async, and this code is very similar to the old working code in structure (I moved a few things around, like the JsonConvert to the bottom layer when it used to be up top). The major changes, in fact, were all at the level that is being mocked, and shouldn't impact the unit test. The unit test is almost identical to the old one (removed some serialization/deserialization).

Exception is a Null Reference Exception on GetCharacterAsync line:

 var obj = res.Result.Values.FirstOrDefault();
inan
  • 160
  • 10
Marshall Tigerus
  • 3,265
  • 7
  • 32
  • 60
  • Possible duplicate of [What is a NullReferenceException, and how do I fix it?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – Igor Oct 17 '16 at 20:27
  • 1
    Could you please post the exception and stack trace? – Andrew Oct 17 '16 at 20:28
  • 1
    You really should not likely be doing `character.Result.Name`, make the test return `async Task` instead of `void` and await `character`. Pretty much every unit test library (including the one built in to visual studio) supports async/await now. – Scott Chamberlain Oct 17 '16 at 20:31
  • You should trace a little bit more your code and provide a concise question about it. Not having us debugging – Miguel Oct 17 '16 at 20:40

1 Answers1

4

Your await requester.CreateGetRequestAsync<Task<Dictionary<long, Character>>> is wrong, you should not be passing Task in to that function, the method signature should be await requester.CreateGetRequestAsync<Dictionary<long, Character>>

public async Task<Character> GetCharacterAsync(Region region, int charId)
{
    var res = await requester.CreateGetRequestAsync<Dictionary<long, Character>>
                   (bld.BuildUrlForEndpoint("GetCharacter",
                   region, charId.ToString()));
    var obj = res.Values.FirstOrDefault();
    return obj;
}

The reason you are getting null is in GetCharacterAsync you call CreateGetRequestAsync<Task<Dictionary<long, Character>>> but your moq was set up with mockReq.Setup(x => x.CreateGetRequestAsync<Dictionary<long, Character>>


P.S. As a side note, you really should not likely be doing character.Result.Name in your test, make the test return async Task instead of void and await the result of api.GetCharacterAsync. Pretty much every unit test library (including the one built in to visual studio) supports async/await now.

public async Task GetCharactersAsync_ById_Test()
{
    var charDictionary = TestHelper.GetCharactersIdDictionary(1);
    var mockReq = new Mock<IRequester>();
    mockReq.Setup(x => x.CreateGetRequestAsync<Dictionary<long, Character>>
                        (It.IsAny<string>())).ReturnsAsync(charDictionary);
    var char1 = TestHelper.GetCharacter();
    var api = GameObject.GetInstance(mockReq.Object);

    var character = await api.GetCharacterAsync(TestHelper.GetRegion(), (int)char.Id);

    Assert.AreEqual(character.Name, char1.Name);
}
Scott Chamberlain
  • 116,967
  • 28
  • 260
  • 389