0

I have a constructor for EdmService that creates an object EdmApiClient in the constructor

public EdmService(IOptions<EdmApiClientOptions> apiOptions, IHttpClientFactory httpClientFactory)
{
    Check.ArgumentIsNotNull(apiOptions, nameof(apiOptions));
    Check.ArgumentIsNotNull(httpClientFactory, nameof(httpClientFactory));
    _client = new EdmApiClient(apiOptions.Value.Hostname, httpClientFactory.CreateClient(nameof(EdmApiClient)));
}

This _client is used in the method that I want to test. But how to mock it?

7
  • if your Sut creates a depency itself, there's no way to mock it. You'd need to override your SuT's constructor and prevent the original from being executed. That's the point of Dependency Injection: don't make your SuT create its dependencies, but inject them from the outside. Commented Oct 6, 2022 at 12:11
  • Hm, so the only chance is to mock stub http client factory that would do fake call inside edmClient? Commented Oct 6, 2022 at 12:14
  • If you don't want to change your SuT to inject the depency, then yes, you can only fake the factory. Commented Oct 6, 2022 at 12:14
  • 1
    You might exchange the object at _client with a mock via reflection after the constructor executed. But still its fighting against code that isn't written for testability but should. If its just used this way presumably EdmApiClient is itself also not written to be easily mockable. Commented Oct 6, 2022 at 12:18
  • 1
    I suggest you read up on Dependency Inversion amd Dependency Injection. This is more than an answer can reasonably encompass. Commented Oct 6, 2022 at 13:13

1 Answer 1

3

You cannot mock it if the service creates a new instance. You must change your service to accept all its dependencies via the constructor:

public EdmService(IOptions<EdmApiClientOptions> apiOptions, IHttpClientFactory httpClientFactory, EdmApiClient client)
{
    Check.ArgumentIsNotNull(apiOptions, nameof(apiOptions));
    Check.ArgumentIsNotNull(httpClientFactory, nameof(httpClientFactory));
    _client = client;
}

public static void Main() {
    var svc = new EdmService(
        apiOptions,
        httpClientFactory,
        new EdmApiClient(
            apiOptions.Value.Hostname,
            httpClientFactory.CreateClient(nameof(EdmApiClient)));
}

If passing the client is not an option, consider passing a factory instead.

public EdmService(IOptions<EdmApiClientOptions> apiOptions, IHttpClientFactory httpClientFactory, Func<String, IHttpClient, EdmApiClient> clientFactory)
{
    Check.ArgumentIsNotNull(apiOptions, nameof(apiOptions));
    Check.ArgumentIsNotNull(httpClientFactory, nameof(httpClientFactory));
    _client = client(apiOptions.Value.Hostname, httpClientFactory.CreateClient(nameof(EdmApiClient)));
}

public static void Main() {
    var svc = new EdmService(
        apiOptions,
        httpClientFactory,
        (hostname, client) => new EdmApiClient(hostname, client));
}

Or use the real EdmApiClient, but have your httpClientFactory return a test double.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.