Moq Verify 说方法从未被调用,但它被调用了

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

我有以下代码,它从 Kafka 读取交易,并更新账户余额以显示该交易

public class KafkaConsumerService : BackgroundService
{
    private readonly IConsumer<string, Transaction> _kafkaConsumer;
    private readonly IRepository _repository;
    private readonly ICalculator _calculator;

    public KafkaConsumerService(
        IConsumer<string, Transaction> kafkaConsumer,
        IRepository repository,
        ICalculator calculator
    )
    {
        _kafkaConsumer = kafkaConsumer;
        _repository = repository;
        _calculator = calculator;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var consumeResult = await Task.Run(() => _kafkaConsumer.Consume(stoppingToken), stoppingToken);
        var transaction = consumeResult.Message.Value;

        var account = await _repository.GetAccount(transaction.Account);
        await _repository.UpdateAccount(_calculator.CalculateAccount(account, Normalize(transaction)));
    }

    private Transaction Normalize(Transaction transaction)
    {
        if (!transaction.IsCancellation)
        {
            return transaction;
        }

        return new Transaction(transaction)
        {
            Amount = transaction.Amount * -1,
            IsCancellation = false
        };
    }
}

然后我使用 XUnit 和 Moq 为此编写了以下单元测试

public class KafkaConsumerServiceTest
{
    private readonly Mock<IConsumer<string, Transaction>> _kafka = new();
    private readonly Mock<IRepository> _repository = new();
    private readonly Mock<ICalculator> _calculator = new();

    private readonly Fixture _fixture = new();
    private readonly KafkaConsumerService _kafkaConsumerService;

    public KafkaConsumerServiceTest()
    {
        _kafkaConsumerService = new KafkaConsumerService(_kafka.Object, _repository.Object, _calculator.Object);
    }

    [Fact]
    public async Task KafkaConsumerService_ProcessesCancelationTransaction()
    {
        _fixture.Customize<Transaction>(composer => composer
            .With(transaction => transaction.IsCancellation, true)
        );

        var transaction = _fixture.Create<Transaction>();
        _kafka
            .Setup(consumer => consumer.Consume(It.IsAny<CancellationToken>()))
            .Returns(new ConsumeResult<string, Transaction>
            {
                Message = new Message<string, Transaction>
                {
                    Value = transaction,
                },
            });

        var result = _fixture.Create<Account>() with
        {
            AccountName = transaction.Account
        };

        _repository
            .Setup(repository => repository.GetAccount(transaction.Account))
            .ReturnsAsync(result);

        _calculator
            .Setup(calculator => calculator.CalculateAccount(It.IsAny<Account?>(), It.IsAny<Transaction>()))
            .Returns(result);

        await _kafkaConsumerService.StartAsync(CancellationToken.None);

        _repository.Verify(repository =>
            repository.GetAccount(transaction.Account)
        );
        _calculator.Verify(calculator =>
            calculator.CalculateAccount(result, transaction)
        );
        _repository.Verify(repository => repository.UpdateAccount(result));
    }
}

但是我得到以下错误

Moq.MockException

Expected invocation on the mock at least once, but was never performed: repository => repository.GetAccount("Account73ccea18-e39c-493f-9533-7af7f983b8ab")

Performed invocations:

   Mock<IRepository:1> (repository):

      IRepository.GetAccount("Account73ccea18-e39c-493f-9533-7af7f983b8ab")
      IRepository.UpdateAccount(Account { AccountName = Account73ccea18-e39c-493f-9533-7af7f983b8ab, Amount = 119 })

正如您所看到的,它说从未调用过 GetAccount("Account73ccea18-e39c-493f-9533-7af7f983b8ab") 方法,但是在它的下方,在执行的调用下,它说它被调用了。

如果有人对这里出了什么问题有任何想法,我将不胜感激。

编辑 在单元测试中添加 await Task.Delay(100) 似乎可以解决问题,但这不是一个理想的解决方案,我仍然不明白为什么会首先出现这个问题。

编辑#2 似乎删除 BackgroundService 的扩展(https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.backgroundservice?view=dotnet-plat-ext-7.0)似乎可以解决测试也是如此。这会不会以某种方式导致我的代码出现竞争条件?

c# moq xunit
1个回答
0
投票

我认为罪魁祸首可能是这个:

return new Transaction(transaction)
{
    Amount = transaction.Amount * -1,
    IsCancellation = false
};

当你验证一个实例时,它正在做一个引用检查,所以它不能是一个不同的新创建的对象。

试试

_repository.Verify(repository =>
    repository.GetAccount(It.IsAny<string>())
);
_repository.Verify(repository => repository.UpdateAccount(It.IsAny<Transaction>()));

您也可以使用

It.Is<Transaction>(t => t.AccountName == "account")
来验证断言中的特定值。

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