我在使用 Moq 和 xUnit 用 C# 编写单元测试时遇到问题。
在我的服务中,我有以下代码:
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential);
return (await graphClient.Users.Request().Filter($"displayName eq '{mobilePhone}'").GetAsync()).FirstOrDefault();
但我不知道模拟
GraphClient
函数的方法:
graphClient.Users.Request().Filter($"displayName eq '{mobilePhone}'").GetAsync()).FirstOrDefault();
根据您的用例和现有代码库,您还可以在构造函数调用中为两个接口提供一些空存根,并使用重写虚拟函数的功能。如果您使用文档中提供的一些模拟框架(例如 Moq),这会派上用场:
// Arrange
var mockAuthProvider = new Mock<IAuthenticationProvider>();
var mockHttpProvider = new Mock<IHttpProvider>();
var mockGraphClient = new Mock<GraphServiceClient>(mockAuthProvider.Object, mockHttpProvider.Object);
ManagedDevice md = new ManagedDevice
{
Id = "1",
DeviceCategory = new DeviceCategory()
{
Description = "Sample Description"
}
};
// setup the call
mockGraphClient
.Setup(g => g.DeviceManagement.ManagedDevices["1"]
.Request()
.GetAsync(CancellationToken.None))
.ReturnsAsync(md)
.Verifiable();
// Act
var graphClient = mockGraphClient.Object;
var device = await graphClient.DeviceManagement.ManagedDevices["1"]
.Request()
.GetAsync(CancellationToken.None);
// Assert
Assert.Equal("1",device.Id);
通过使用这种方法,您不必为在线上完成的具体 HTTP 请求而烦恼。相反,您可以简单地覆盖(嵌套)方法调用及其参数,并定义返回的对象,而无需序列化/反序列化步骤。另请注意,在模拟中您可以使用例如
It.IsAny<string>()
和类似的构造来定义您是否需要精确的参数检查或其他内容。
我尝试使用第一个建议的解决方案来解决此问题,但这不适用于 Microsoft.Graph 5.11.0,因为 Moq 无法覆盖该版本中
GraphServiceClient
上的方法,但我设法通过模拟来实现类似的行为 IRequestAdapter
代替。
// Arrange
var mockBaseUrl = "";
var mockRequestAdapter = new Mock<IRequestAdapter>();
var mockGraphServiceClient = new Mock<GraphServiceClient>(
mockRequestAdapter.Object,
mockBaseUrl
);
mockRequestAdapter
.Setup(
a =>
a.SendAsync(
It.Is<RequestInformation>(
ri => (string)ri.PathParameters["managedDevice%2Did"] == "1"
),
It.IsAny<ParsableFactory<ManagedDevice>>(),
It.IsAny<Dictionary<string, ParsableFactory<IParsable>>?>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(
new ManagedDevice()
{
Id = "1",
DeviceCategory = new DeviceCategory() { Description = "Sample description" }
}
);
// Act
var device = await mockGraphServiceClient.Object.DeviceManagement.ManagedDevices[
"1"
].GetAsync();
// Assert
Assert.NotNull(device);
Assert.Equal("1", device!.Id);
如果您尝试使用此解决方案,请记住更改 id 路径参数的名称以匹配您请求的类型。命名约定是
$"{camelCaseTypeName}%2Did"
。
我使用更通用的方法为 Microsoft Graph API SDK v5 创建了以下固定装置。对于我的用例,我只需要获取数据,但可以为post创建类似的设置。
/// <summary>
/// Graph email service fixture.
/// </summary>
internal class GraphEmailServiceFixture
{
internal readonly Mock<GraphServiceClient> GraphServiceClientMock;
private readonly Mock<IRequestAdapter> _requestAdapterMock;
internal GraphEmailServiceFixture()
{
_requestAdapterMock = new Mock<IRequestAdapter>();
GraphServiceClientMock = new Mock<GraphServiceClient>(_requestAdapterMock.Object, It.IsAny<string>());
}
public IGraphEmailService CreateSut() => new GraphEmailService(
GraphServiceClientMock.Object);
/// <summary>
/// Sets up a mock for SendAsync on the GraphEmailService.RequestAdapter, which returns a GraphServiceClient with response.
/// </summary>
/// <param name="response">Graph API response.</param>
/// <returns>The fixture itself - used for chaining when creating fixture.</returns>
internal GraphEmailServiceFixture WithReturnForGraphClientResponse<ModelType>(ModelType response) where ModelType : IParsable
{
_requestAdapterMock
.Setup(ra => ra.SendAsync(It.IsAny<RequestInformation>(), It.IsAny<ParsableFactory<ModelType>>(), It.IsAny<Dictionary<string, ParsableFactory<IParsable>>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(response);
return this;
}
}
GraphEmailService
是包含从 Graph API 获取数据的所有方法的服务。 GraphServiceClient
需要一个构造函数参数。
使用方法如下:
[TestMethod]
public async Task GetMessage_WithAttachments_Expects()
{
// Arrange.
var message = new Message();
var attachments = new AttachmentCollectionResponse
{
Value = new List<Attachment>
{
new FileAttachment()
}
};
var sut = new GraphEmailServiceFixture()
.WithReturnForGraphClientResponse(message)
.WithReturnForGraphClientResponse(attachments)
.CreateSut();
// Act.
var result = await sut.GetMessage();
// Assert.
Assert.IsNotNull(result);
Assert.AreEqual(output, result);
}