C# 模拟单元测试 GraphServiceClient

问题描述 投票:0回答:3

我在使用 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();
c# azure moq xunit
3个回答
9
投票

根据您的用例和现有代码库,您还可以在构造函数调用中为两个接口提供一些空存根,并使用重写虚拟函数的功能。如果您使用文档中提供的一些模拟框架(例如 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>()
和类似的构造来定义您是否需要精确的参数检查或其他内容。


2
投票

我尝试使用第一个建议的解决方案来解决此问题,但这不适用于 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"


0
投票

我使用更通用的方法为 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);
}
© www.soinside.com 2019 - 2024. All rights reserved.