Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock returns null value when ReturnResult is a custom object but works as expected when it is a primitive type bool

I am using Moq in .net core(1.1) and having a bit of torrid time understanding this behavior as all the examples on interweb points to the fact the this should work with no issues.

I have already tried with:

  • Returns(Task.FromResult(...)
  • Returns(Task.FromResult(...)
  • ReturnsAsync(...)

Mocking a IHttpClient interface to wrap PostAsync, PutAsync and GetAsync. All of these return an ApiResponse object.

var mockClient = new Mock<IHttpClient>();

Does not work:

mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null))
                                    .Returns(Task.FromResult(new ApiResponse()));

PostSync definition:

public async Task<ApiResponse> PostAsync(string url, string body, string authToken = null)

Does work:

mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null))
                                     .Returns(Task.FromResult(bool));

PostSync definition:

public async Task<bool> PostAsync(string url, string body, string authToken = null)

Usage:

var api = new ApiService(mockClient.Object);
var response = api.LoginAsync(body.Username, body.Password);

UPDATE

[Fact]
public async void TestLogin()
{
   var mockClient = new Mock<IHttpClient>();
   mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null)).Returns(Task.FromResult(new ApiResponse()));

   var api = new ApiService(mockClient.Object);
   var response = await api.LoginAsync(body.Username, body.Password);
   Assert.IsTrue(response);
}

Return Type:

public class ApiResponse
{
     public string Content { get; set; }

     public HttpStatusCode StatusCode { get; set; }

     public string Reason { get; set; }
}

LoginAsync:

public async Task<bool> LoginAsync(string user, string password)
{
    var body = new { Username = user, Password = password };
    try
    {
        var response = await _http.PostAsync(_login_url, JsonConvert.SerializeObject(body), null);
        return response .State == 1;
    }
    catch (Exception ex)
    {
        Logger.Error(ex);
        return false;
    }
 }

PostAsync:

public async Task<object> PostAsync(string url, string body, string authToken = null)
{
    var client = new HttpClient();
    var content = new StringContent(body, Encoding.UTF8, "application/json");
    var response = await client.PostAsync(new Uri(url), content);
    var resp = await response.Result.Content.ReadAsStringAsync();
    return new ApiResponse
    {
        Content = resp,
        StatusCode = response.Result.StatusCode,
        Reason = response.Result.ReasonPhrase
    };
}
like image 638
Walaitki Avatar asked Nov 22 '25 22:11

Walaitki


1 Answers

Assuming a simple method under test like this based on minimal example provided above.

public class ApiService {
    private IHttpClient _http;
    private string _login_url;

    public ApiService(IHttpClient httpClient) {
        this._http = httpClient;
    }

    public async Task<bool> LoginAsync(string user, string password) {
        var body = new { Username = user, Password = password };
        try {
            var response = await _http.PostAsync(_login_url, JsonConvert.SerializeObject(body), null);
            return response.StatusCode == HttpStatusCode.OK;
        } catch (Exception ex) {
            //Logger.Error(ex);
            return false;
        }
    }
}

The following test works when configured correctly

[Fact]
public async Task Login_Should_Return_True() { //<-- note the Task and not void
    //Arrange
    var mockClient = new Mock<IHttpClient>();
    mockClient
        .Setup(x => x.PostAsync(It.IsAny<string>(), It.IsAny<string>(), null))
        .ReturnsAsync(new ApiResponse() { StatusCode = HttpStatusCode.OK });

    var api = new ApiService(mockClient.Object);

    //Act
    var response = await api.LoginAsync("", "");

    //Assert
    Assert.IsTrue(response);
}

The above is just for demonstrative purposes only to show that it can work provided the test is configured properly and exercised based on the expected behavior.

Take some time and review the Moq quick start to get a better understanding of how to use the framework.

like image 187
Nkosi Avatar answered Nov 28 '25 16:11

Nkosi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!