我正在尝试在 .NET 6 中测试命令处理程序。我必须添加 DynamicProxyGenAssembly2 属性。怎么去除呢?

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

我在我的 .NET 6 项目中使用了 CQRS 模式。我有一个

CreateQueryCommandHandler
。它的访问修饰符设置为
internal
我无法更改。我正在尝试对我的命令处理程序进行单元测试。

我在我的应用程序项目中进行了更改,其中包含我的命令,以提供对我的单元测试项目的内部类访问权限。但仅仅添加这个并不能解决问题。

我也必须补充

[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=3483847384783478347.....

如果没有将此行添加到我的创建命令处理程序类中,我会收到此错误:

System.ArgumentException:无法为 Microsoft.Extensions.Logging.ILogger`1[[QueryStore.Application.Queries.CreateQueryCommandHandler, QueryStore.Application, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] 创建代理,因为类型 QueryStore.Application.Queries.CreateQueryCommandHandler 不可访问。将其设为公开或内部,并使用 [程序集:InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000....")] 属性标记程序集,因为程序集 Microsoft.Extensions.Logging.Abstractions 是强名称的。 (参数“interfaceToProxy”)

堆栈跟踪:

在 Castle.DynamicProxy.DefaultProxyBuilder.AssertValidTypeForTarget(类型类型,类型目标,字符串参数名称)
在 Castle.DynamicProxy.DefaultProxyBuilder.AssertValidTypeForTarget(类型类型,类型目标,字符串 paramName)
在 Castle.DynamicProxy.DefaultProxyBuilder.AssertValidType(类型目标,字符串 paramName)
在Castle.DynamicProxy.DefaultProxyBuilder.CreateInterfaceProxyTypeWithoutTarget(类型interfaceToProxy,类型[]additionalInterfacesToProxy,ProxyGenerationOptions选项) 在Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyTypeWithoutTarget(类型interfaceToProxy,类型[]additionalInterfacesToProxy,ProxyGenerationOptions选项) 在Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithoutTarget(类型interfaceToProxy,类型[]additionalInterfacesToProxy,ProxyGenerationOptions选项,IInterceptor[]拦截器) 在//src/Moq/Interception/CastleProxyFactory.cs中的Moq.CastleProxyFactory.CreateProxy(类型mockType,IInterceptor拦截器,Type []接口,Object []参数):第98行 在 Moq.Mock

1.InitializeInstance() in /_/src/Moq/Mock
1.cs:第 502 行 在 Moq.Mock
1.OnGetObject() in /_/src/Moq/Mock
1.cs:第 516 行 在 /
/src/Moq/Mock.cs 中的 Moq.Mock.get_Object() 处:第 180 行 在 Moq.Mock
1.get_Object() in /_/src/Moq/Mock
1.cs:第 453 行 在 /home/hardik/Desktop/queryhub/src/Application/tests/Queries/CreateQueryCommandHandlerTests.cs 中的 QueryStore.Application.UnitTests.Queries.Commands.CreateQueryCommandHandlerTests.Handle_Should_ReturnSuccessResult_WhenQueryIsAddedToDB() 处:第 127 行 在 /home/hardik/Desktop/queryhub/src/Application/tests/Queries/CreateQueryCommandHandlerTests.cs 中的 QueryStore.Application.UnitTests.Queries.Commands.CreateQueryCommandHandlerTests.Handle_Should_ReturnSuccessResult_WhenQueryIsAddedToDB() 处:第 133 行 在 System.Threading.Tasks.Task.<>c.b__128_0(对象状态)

失败的 QueryStore.Application.UnitTests.Queries.Commands.CreateQueryCommandHandlerTests.Handle_Should_ReturnErrorResult_WhenUnknownErrorOccurs [151 毫秒]

错误信息:
System.ArgumentException :无法为类型 Microsoft.Extensions.Logging.ILogger`1[[QueryStore.Application.Queries.CreateQueryCommandHandler, QueryStore.Application, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] 创建代理,因为类型QueryStore.Application.Queries.CreateQueryCommandHandler 不可访问。将其设为公开或内部,并使用 [程序集:InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000....")] 属性标记程序集,因为程序集 Microsoft.Extensions.Logging.Abstractions 是强名称的。 (参数“interfaceToProxy”)

添加动态代理后,所有测试用例都可以正常工作。如果有人能向我解释为什么这是有效的,我将不胜感激。如果可能的话如何删除它。使我的代码更加简洁。我也希望有更好的测试策略。

我的

CommandHandler
代码:

using FluentValidation;
using MediatR;
using Microsoft.Extensions.Logging;
using QueryStore.Application.Common;
using System.Data.Common;

namespace QueryStore.Application.Queries;

internal sealed class CreateQueryCommandHandler : IRequestHandler<CreateQueryCommand, Result<string>>
{
    private readonly IQueryStoreDbContext _context;
    private readonly IValidator<CreateQueryCommand> _validator;
    private readonly ILogger _logger;

    public CreateQueryCommandHandler(
        IQueryStoreDbContext context,
        IValidator<CreateQueryCommand> validator,
        ILogger<CreateQueryCommandHandler> logger)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
        _validator = validator ?? throw new ArgumentNullException(nameof(validator));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task<Result<string>> Handle(CreateQueryCommand request, CancellationToken cancellationToken)
    {
        var validationResult = await _validator.ValidateAsync(request, cancellationToken);

        if (!validationResult.IsValid)
        {
            return Result.Error(validationResult.Errors.Select(e => new Error($"{e.PropertyName}", e.ErrorMessage)));
        }

        try
        {
            var entity = request.AsQuery();

            _context.Queries.Add(entity);

            await _context.SaveChangesAsync(cancellationToken: cancellationToken);

            return Result<string>.Success(entity.Id);
        }
        catch (DbException ex)
        {
            _logger.LogError(ex, "An error occurred while saving query to database");

            return Result.Error(new Error("error", ex.Message));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An unknown error occurred while saving query");

            return Result.Error(new Error("error", ex.Message));
        }
    }
}

单元测试代码:

using System.Data.Common;
using FluentAssertions;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using QueryStore.Application.Queries;
using QueryStore.Domain;

namespace QueryStore.Application.UnitTests.Queries.Commands;

public class CreateQueryCommandHandlerTests
{
    private readonly Mock<IQueryStoreDbContext> _contextMock = new Mock<IQueryStoreDbContext>();
    private readonly IValidator<CreateQueryCommand> _validator = new CreateQueryCommandValidator();
    private readonly Mock<ILogger<CreateQueryCommandHandler>> _loggerMock =
        new Mock<ILogger<CreateQueryCommandHandler>>();
    
    [Fact]
    public async void Handle_Should_ReturnSuccessResult_WhenQueryIsAddedToDB()
    {
        // Arrange
        var command = new CreateQueryCommand()
        {
            Name = "Some Name",
            Content = "Select * from AbcCompany",
            Description = "Some description about the query.",
            Tags = new[] { "company", "import" }
        };

        var options = new DbContextOptionsBuilder<QueryStoreDbContext>()
            .UseInMemoryDatabase("TestDatabase")
            .Options;

        await using var dbContext = new QueryStoreDbContext(options);
        
        // Act
        var validationResult = await _validator.ValidateAsync(command);
            
        var handler = new CreateQueryCommandHandler(dbContext, _validator, _loggerMock.Object);
            
        var result = await handler.Handle(command, cancellationToken: CancellationToken.None);
            
        // Assert
        validationResult.IsValid.Should().BeTrue();
        result.IsSuccess.Should().BeTrue();
    }
    
    [Fact]
    public async Task Handle_Should_ReturnErrorResult_WhenUnknownErrorOccurs()
    {
        // Arrange
        var command = new CreateQueryCommand()
        {
            Name = "Some Name",
            Content = "Select * from AbcCompany",
            Description = "Some description about the query.",
            Tags = new[] { "company", "import" }
        };

        _contextMock.Setup(context =>
            context.Queries.Add(It.IsAny<Query>())
        ).Throws(new Exception("Unknown error"));

        // Act
        var handler = new CreateQueryCommandHandler(_contextMock.Object, _validator, _loggerMock.Object);
        var result = await handler.Handle(command, cancellationToken: CancellationToken.None);

        // Assert
        result.IsSuccess.Should().BeFalse();
        result.Errors.Should().NotBeEmpty();
    }   
}
c# .net unit-testing mocking
1个回答
0
投票
  • 您在测试中使用
    Moq
  • Moq
    内部使用
    Castle.DynamicProxy
    库来生成模拟
  • Castle.DynamicProxy
    在运行时发出模拟代码,并将该代码放置在名为
    DynamicProxyGenAssembly2
  • 的动态创建的程序集中
  • 运行时发出的代码应该能够访问您正在模拟的原始实体,包括
    CreateQueryCommandHandler
  • 因此,要使其正常工作,您需要添加提到的属性以允许动态生成的代码访问内部成员。

根据你的第二个问题 - 我不知道更好的测试方法不需要该属性。

© www.soinside.com 2019 - 2024. All rights reserved.