如何使用 Moq 在 .NET 6 的以下代码中模拟方法?

How to mock methods in the below code in the .NET 6 using Moq?

下面是需要测试的InvokeAsync方法。

    public async Task<bool> InvokeAsync(Batch batch)
    {
        var refundRequests = await this.RefundRepository.GetsAsync(batch.RefundRequests.Select(x => x.Id));

        refundRequests.RemoveAll(x => x.Status != RefundRequestStatus.Approved);
        var distributions = await DistributionRepository.GetsAsync(refundRequests.Select(x => x.Distribution.Id));

        var bundles = await this.BundleRepository.GetsAsync(distributions.Select(x => x.BundleId));

        foreach (var getRefundRequest in refundRequests)
        {
            var distribution = distributions.FirstOrDefault(x => x.Id == getRefundRequest.Distribution.Id);

            if (distribution?.BundleId != null)
            {
                var bundle = bundles.First(x => x.Id == distribution?.BundleId);

                Bundle result = await Reverse.InvokeAsync(getRefundRequest, distribution, bundle); //MOCK
            }
            getRefundRequest.Status = RefundRequestStatus.Closed;
            getRefundRequest.LastUpdatedBy = "Test User";

            bool isUpdated = await UpdateRefund.InvokeAsync(getRefundRequest); //MOCK
        }

        batch.Status = BatchStatus.Posted;
        batch.LastUpdatedBy = "Test User";
        var isSuccess = await UpdateBatch.InvokeAsync(batch); //MOCK

        return isSuccess;
    }

单元测试方法

[Fact]
public async Task Post_Batch()
{
    foreach (var refundBatch in Factory.Batch.CreateRefundBatchesData())
    {
        var refundRequests = await this.RefundRequestRepository.GetsAsync(batch.RefundRequests.Select(x => x.Id));
        var distributions = await this.DistributionRepository.GetsAsync(refundRequests.Select(x => x.Distribution.Id));
        var bundles = await this.BundleRepository.GetsAsync(distributions.Select(x => x.BundleId));

        for (int i = 0; i < refundRequests.Count; i++)
        {
            var refundRequest = refundRequests[i];
            var bundle = bundles[i];
            var distribution = distributions[i];
            MockSetupReverse(refundRequest, distribution, bundle);
            MockSetupUpdateRefund(refundRequest);
        }

        MockSetupUpdateBatch(batch);

        //Act
        var postRefund = await UseCase.InvokeAsync(batch);

        //Assert
        postRefund.ShouldNotBeNull();
        postRefund.IsPosted.ShouldBeTrue();
    }
}

MockSetup 方法

private void MockSetupReverse(RefundRequest refundRequest, Distribution distribution, Bundle bundle)
{
    this.MockReverse
        .Setup(x => x.InvokeAsync(refundRequest, distribution, bundle))
        .Returns(async () => 
        {
            bundle.Status = BundleStatus.Closed;
            return await Task.Run(() => bundle); 
        });
}

private void MockSetupUpdateRefund(RefundRequest refundRequest)
{
    this.MockUpdateRefund
            .Setup(x => x.InvokeAsync(refundRequest))
            .Returns(async () =>
            {
                refundRequest.Status = RefundRequestStatus.Closed;
                refundRequest.LastUpdatedBy = Factory.RefundRequest.TestUserName;
                return await Task.Run(() => true);
            });
}

private void MockSetupUpdateBatch(Batch batch)
{
    this.MockUpdateBatch
            .Setup(x => x.InvokeAsync(batch))
            .Returns(async () =>
            {
                refundBatch.Status = BatchStatus.Posted;
                refundBatch.LastUpdatedBy = Factory.RefundRequest.TestUserName;
                return await Task.Run(() => true);
            });
}

UpdateBatch 的模拟是 working 和 returns true when该方法被调用。但是,当相应的方法是调用。有什么想法吗?

如果需要更多信息来支持该问题,请告诉我。

当您 Setup 使用特定参数模拟时,Returns 仅在使用此特定参数时适用。

UpdateBatch 起作用的原因是因为您在模拟和被测 class 中使用了对相同 Batch 的相同引用:

MockSetupUpdateBatch(batch);

//Act
var postRefund = await UseCase.InvokeAsync(batch); // <---- Same "batch"

当您的测试代码调用 RefundRequestRepository.GetsAsync 时,您可能会得到与被测试的 class 调用 GetsAsync 时不同的结果,因此设置与被测试的 [=] 的调用无关=27=] 并且可能 returns 默认布尔值 (false).

有关如何正确模拟的更多信息,请参阅 this GitHub page