Closed amerjusupovic closed 4 weeks ago
Hi @amerjusupovic. Can you help me understand the end-to-end scenario of what you're trying to test?
Unless you've got an unusual scenario, you shouldn't need to mock the extensions, but would be mocking the client method to return your own pagable, in a pattern similar to what is demonstrated in [Unit testing and mocking with the Azure SDK for .NET](https://learn.microsoft.com/dotnet/azure/sdk/unit-testing-mocking?tabs=moq#explore-paging:
Page<SecretProperties> page1 = Page<SecretProperties>.FromValues(
new[]
{
new SecretProperties("secret1"),
new SecretProperties("secret2")
},
"continuationToken",
Mock.Of<Response>());
Page<SecretProperties> page2 = Page<SecretProperties>.FromValues(
new[]
{
new SecretProperties("secret3"),
new SecretProperties("secret4")
},
"continuationToken2",
Mock.Of<Response>());
Page<SecretProperties> lastPage = Page<SecretProperties>.FromValues(
new[]
{
new SecretProperties("secret5"),
new SecretProperties("secret6")
},
continuationToken: null,
Mock.Of<Response>());
Pageable<SecretProperties> pageable = Pageable<SecretProperties>
.FromPages(new[] { page1, page2, lastPage });
AsyncPageable<SecretProperties> asyncPageable = AsyncPageable<SecretProperties>
.FromPages(new[] { page1, page2, lastPage });
Hi @amerjusupovic. Thank you for opening this issue and giving us the opportunity to assist. To help our team better understand your issue and the details of your scenario please provide a response to the question asked above or the information requested above. This will help us more accurately address your issue.
Thanks, my scenario is that I want to test my code that calls ConfigurationClient.GetConfigurationSettingsAsync.AsPages(IEnumerable<MatchConditions>)
. Before, I only had calls to AsPages()
without any params and that was fine because I could create a mock of AsyncPageable<ConfigurationSetting>
and implement a mock AsPages
method. However, that doesn't work anymore because I'm not allowed to mock an extension method in Moq
and I can't just create a new AsPages
on the mock pageable like I have before, since it won't be an extension.
For example, I can't do this:
var mockClient = new Mock<ConfigurationClient>(MockBehavior.Strict);
mockClient.Setup(c => c.GetConfigurationSettingsAsync(
It.IsAny<SettingSelector>(),
It.IsAny<CancellationToken>())
.AsPages(
It.IsAny<IEnumerable<MatchConditions>>(),
It.IsAny<string>(),
It.IsAny<int>()))
// rest of setup
and I can't just mock GetConfigurationSettingsAsync
because when it calls the AsPages(IEnumerable<MatchConditions>)
extension method, it throws InvalidOperationException
.
Let me know if that makes sense or if there's a different way to test the extension.
@amerjusupovic: In this case, you'd want to combine the approach that I described above along with a custom mock extension method. By defining your extension method in the same namespace as the code calling it and using actual types rather than open generics, it will be the implementation that the compiler prefers.
for example:
Page<ConfigurationSetting> page1 = Page<ConfigurationSetting>.FromValues(
new[]
{
new ConfigurationSetting("secret1", "val1"),
new ConfigurationSetting("secret2", "val2")
},
"continuationToken",
Mock.Of<Response>());
Page<ConfigurationSetting> page2 = Page<ConfigurationSetting>.FromValues(
new[]
{
new ConfigurationSetting("secret3", "val3"),
new ConfigurationSetting("secret4", "val4")
},
"continuationToken2",
Mock.Of<Response>());
Page<ConfigurationSetting> lastPage = Page<ConfigurationSetting>.FromValues(
new[]
{
new ConfigurationSetting("secret5", "val5"),
new ConfigurationSetting("secret6", "val6")
},
continuationToken: null,
Mock.Of<Response>());
AsyncPageable<ConfigurationSetting> asyncPageable = AsyncPageable<ConfigurationSetting>
.FromPages(new[] { page1, page2, lastPage });
var mockClient = new Mock<ConfigurationClient>();
mockClient
.Setup(c => c.GetConfigurationSettingsAsync(
It.IsAny<SettingSelector>(),
It.IsAny<CancellationToken>()))
.Returns(asyncPageable);
// Invoke the mocks
var mockPagable = mockClient.Object.GetConfigurationSettingsAsync(new(), default);
var mockPages = mockPagable.AsPages(new[] { new MatchConditions() });
// Mock extensions with more specific typing and namespace locality.
public static class MockExtensions
{
public static async IAsyncEnumerable<Page<ConfigurationSetting>> AsPages(
this AsyncPageable<ConfigurationSetting> instance,
IEnumerable<MatchConditions> matches,
string continuationToken = null,
int? pageHint = null)
{
await foreach (var page in instance.AsPages())
{
yield return page;
}
}
}
Hi @amerjusupovic. Thank you for opening this issue and giving us the opportunity to assist. We believe that this has been addressed. If you feel that further discussion is needed, please add a comment with the text "/unresolve" to remove the "issue-addressed" label and continue the conversation.
Hi @amerjusupovic, since you haven’t asked that we /unresolve
the issue, we’ll close this out. If you believe further discussion is needed, please add a comment /unresolve
to reopen the issue.
Library name and version
Azure.Data.AppConfiguration 1.4.1
Query/Question
When writing unit tests using Moq there's no way to mock extension methods, and calling this method results in an InvalidOperationException because it tries to cast the pageable object as AsyncConditionalPageable, which is an internal class.
I see this issue is similar, but the tracking issue mentioned in this comment is closed, and it seemed like this might need attention as well.
Environment
.NET SDK: Version: 8.0.300 Commit: 326f6e68b2 Workload version: 8.0.300-manifests.ca8b4b2d MSBuild version: 17.10.4+10fbfbf2e
Runtime Environment: OS Name: Windows OS Version: 10.0.22631 OS Platform: Windows RID: win-x64 Base Path: C:\Program Files\dotnet\sdk\8.0.300\
Visual Studio 2022 - Version 17.10.2