-
Notifications
You must be signed in to change notification settings - Fork 232
feat: Make DownstreamApi public to allow easy mocking in tests #3302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
@@ -453,7 +453,7 @@ public Task<HttpResponseMessage> CallApiForAppAsync( | |||
} | |||
} | |||
|
|||
internal /* for tests */ async Task<HttpResponseMessage> CallApiInternalAsync( | |||
protected virtual /* for tests */ async Task<HttpResponseMessage> CallApiInternalAsync( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jmprieur the only required change (the rest are formatting from vscode...)
@Meir017 : would you like to provide a test, showing how you would use this? |
@jmprieur sure - consider a sample service public class MyService
{
private readonly IDownstreamApi _downstreamApi;
private readonly ILogger<MyService> _logger;
private const string SampleApiName = "MySampleApi"; // Sample API name
public MyService(IDownstreamApi downstreamApi, ILogger<MyService> logger)
{
_downstreamApi = downstreamApi ?? throw new ArgumentNullException(nameof(downstreamApi));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<string> GetDataFromDownstreamApi(object payload = null)
{
try
{
var result = await _downstreamApi.CallApiForUserAsync(SampleApiName, options =>
{
if (payload != null)
{
options.HttpMethod = HttpMethod.Post;
options.Content = JsonContent.Create(payload);
}
});
if (result.StatusCode == HttpStatusCode.OK)
{
return await result.Content.ReadAsStringAsync();
}
else
{
_logger.LogError($"Downstream API call failed with status code: {result.StatusCode}");
return null;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error calling downstream API.");
return null;
}
}
} and example unit-tests public class MyServiceTests
{
[Fact]
public async Task GetDataFromDownstreamApi_Success_ReturnsData()
{
// Arrange
var mockDownstreamApi = new Mock<IDownstreamApi>();
var mockLogger = new Mock<ILogger<MyService>>();
var expectedData = "{\"key\": \"value\"}";
var mockHttpResponse = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(expectedData)
};
// a bit confusing finding the example method to mock
mockDownstreamApi.Setup(api => api.CallApiForUserAsync(
"MySampleApi", // Using the sample API name
It.IsAny<Action<DownstreamApiOptions>>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(mockHttpResponse);
var service = new MyService(mockDownstreamApi.Object, mockLogger.Object);
// Act
var result = await service.GetDataFromDownstreamApi();
// Assert
Assert.Equal(expectedData, result);
// a bit confusing finding the example method to verify
mockDownstreamApi.Verify(api => api.CallApiForUserAsync(
"MySampleApi",
It.IsAny<Action<DownstreamApiOptions>>(),
It.IsAny<CancellationToken>()), Times.Once);
}
// NEW WAY
[Fact]
public async Task GetDataFromDownstreamApi_Success_ReturnsData_UsingCallApiInternalAsync()
{
// Arrange
var mockDownstreamApi = new Mock<IDownstreamApi>();
var mockLogger = new Mock<ILogger<MyService>>();
var expectedData = "{\"key\": \"value\"}";
var mockHttpResponse = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(expectedData)
};
// a bit confusing finding the example method to mock
mockDownstreamApi.Setup(api => api.CallApiInternalAsync(
"MySampleApi",
It.IsAny<DownstreamApiOptions>(),
It.IsAny<bool>()),
It.IsAny<JsonContent>(),
It.IsAny<ClaimsPrincipal>(),
It.IsAny<CancellationToken>())
.ReturnsAsync(mockHttpResponse);
var service = new MyService(mockDownstreamApi.Object, mockLogger.Object);
// Act
var result = await service.GetDataFromDownstreamApi();
// Assert
Assert.Equal(expectedData, result);
mockDownstreamApi.Verify(api => api.CallApiInternalAsync(
"MySampleApi",
It.IsAny<DownstreamApiOptions>(),
It.IsAny<bool>()),
It.IsAny<JsonContent>(),
It.IsAny<ClaimsPrincipal>(),
It.IsAny<CancellationToken>(), Times.Once);
}
} having a single method that is well defined that will be used for mocking will minimize the mocking to a minimum (still running the |
in your sample you use a Mock? not the new virtual? |
Oh, my bad. The idea would be to mock the virtual method instead. **modified the code sample to highlight the new approach |
closes #3199