当单元测试时,是使用验证间接测试此示例中确定的方法

本文关键字:方法 测试 单元测试 验证 | 更新日期: 2023-09-27 17:57:24

我想在下面的代码段中测试FindPolicyToBeResent()。我有几个可用的选择,但想知道如果我的方法,即最后一个选项可以,其他人也会如何处理这种情况?

  • 将 FindPolicyToBeResent() 设为公开。这不是一个选项,因为它仅出于测试的原因公开了实现并使接口混乱
  • 仅使用公共 API 进行单元测试,但这可能很困难,因为我从不公开我直接在 return 语句中过滤的集合,并且出于安全原因无法这样做。这意味着我只能进行有限的测试
  • 通常在这种情况下,
  • 我会将代码分解为一个新对象,但在这种情况下感觉不对,我无法预见此过滤器会在系统的其他任何地方重用,因此它不会比公开该方法更好,并且在任何人用单一责任棒击中我之前(这是合理的), 编码是一种平衡行为,我觉得它会打破保持简单原则。感觉就像创建一个类来为测试服务,而不是实际上有一个单独的单一责任。此外,它还会导致文件和类膨胀。
  • 我可以使代码成为 IEnumerable 的扩展方法,这将使它可测试,但我再次无法预见这个过滤器在其他任何地方使用,所以如果它保留在类中会更有意义
  • 最后一个选项和首选,但可能被视为有点黑客是测试文档ResenderRepository.CanPolicyBeResent(policy)的模拟。Id) 使用 verify() 进一步向下代码,以查看它被蜜蜂击中了多少次。我不确定这是否是个好主意?思潮?

我更喜欢最后一个选项,但它确实感觉有点脏,我在底部有一个示例

public class DocumentResendService : IDocumentResendService
{
    #region Member Variables
    ...
    #endregion

    #region Constructors
    ...
    #endregion

    #region Accessors
    ...
    #endregion
    #region Methods
    public IDocumentResendResponse ResendDocuments()
    {
        if (!IsInputsValid())
        {
            return response;
        }

        RecordRequestAttempt();
        if (RequestLimitIsReached())
        {
            return response;
        }

        FindPolicyToBeResent();

        if(PolicyCanNotBeResent())
        {
            return response;
        }
        RequestDocumentToBeResent();
        return response;
    }

    private bool IsInputsValid()
    {
        ..
    }

    private void RecordRequestAttempt()
    {
        ...
    }

    private bool RequestLimitIsReached()
    {
        ...
    }
    // I want to test this method which basically just filters the policies
    private void FindPolicyToBeResent()
    {
        var allPolicies = policyDocumentRepository.GetPolicy(AgentCode, Email, PostCode, SearchDate, BirthDate);
        policies = allPolicies.Where(currentPolicy => currentPolicy.IsActive() || currentPolicy.IsInTheFuture());
        if (policies.Count() == 0 )
        {
            policies = allPolicies.FilterToPolicyThatHasEndedMostRecently();          
        }
     }

    private bool PolicyCanNotBeResent()
    {
        if (policies == null || !policies.Any())
        {
            response.Add(ErrorCode.PolicyCanNotBeFound);
            return true;
        }
        foreach (var policy in policies)
        {
           // I could mock this line and use a verify here which policy id's are passed in
            if (documentResenderRepository.CanPolicyBeResent(policy.Id) == false)
            {
                response.Add(ErrorCode.UnableToResendDocument);
            }  
        }
        return response.Errors.Any();
    }

    private void RequestDocumentToBeResent()
    {
        ...
    }
    #endregion
}

这是最后一个选项的单元测试解决方案,但

[TestFixture]
public class FindPolicyToBeResentTest : DocumentResenderTestsBase
{
    private readonly List<Policy> allPolicies = new List<Policy>();
    public FindPolicyToBeResentTest()
    {
        var day = -250;
        for (int i = 1; i < 6; i++)
        {
            var policy = new Policy
            {
                Id = i,
                StartDate = DateTime.Now.AddDays(day)
            };
            day = day + 100;
            policy.EndDate = DateTime.Now.AddDays(day);
            allPolicies.Add(policy);
        }
    }
    private void SetUpDocumentResender(IEnumerable<Policy> policies)
    {

        SetUpObjectDefaultsForDocumentResenderCreation();
        policyRepositoryMock.Setup(y => y.GetPolicy(It.IsAny<string>(),
                                                    It.IsAny<string>(),
                                                    It.IsAny<string>(),
                                                    It.IsAny<DateTime>(),
                                                    It.IsAny<DateTime>()))
            .Returns(policies);

        documentResendService = CreateDocumentResendService();
        SetDocumentResenderDefaults();
    }

    [Test]
    public void PoliciesThatAreNotActiveOrAreInThePastShouldBeFilteredOut()
    {
        SetUpDocumentResender(allPolicies);
        documentResendService.ResendDocuments();
        foreach (var policy in allPolicies)
        {
            if (policy.IsActive() || policy.IsInTheFuture())
            {
                documentResenderRepositoryMock.Verify(x => x.CanPolicyBeResent(policy.Id), Times.AtLeastOnce());
            }
            else
            {
                documentResenderRepositoryMock.Verify(x => x.CanPolicyBeResent(policy.Id), Times.Never());
            }
        }
    }
    [Test]
    public void IfNoPoliciesAreFoundThatAreSuitableForDocumentResendingThenGetThePolicyThatHasMostRecentlyExpired()
    {
        var unsuitablePolicies = allPolicies.Where(x => x.IsActive() == false && x.IsInTheFuture() == false).OrderBy(x => x.EndDate);
        var policyWithClosestToEndDateToNow = unsuitablePolicies.ToList().Last();
        SetUpDocumentResender(unsuitablePolicies);
        documentResendService.ResendDocuments();
        documentResenderRepositoryMock.Verify(x => x.CanPolicyBeResent(policyWithClosestToEndDateToNow.Id), Times.AtLeastOnce());
        foreach (var policy in allPolicies.Where(policy => policy != policyWithClosestToEndDateToNow))
        {
            documentResenderRepositoryMock.Verify(x => x.CanPolicyBeResent(policy.Id), Times.Never());
        }
    }
}

当单元测试时,是使用验证间接测试此示例中确定的方法

通过公共方法测试私有方法很好。 如果你的代码足够模块化,那么不应该有太多的设置代码,以便获得正确的条件进入你的私有方法。 如果你发现自己设置了很多东西只是为了进入你的私人方法,那么你很可能在一堂课上做得太多了。

在你的情况下,我很想接受你的第3点),并创建一个PolicyFinder:IPolicyFinder类。 也许你现在不需要重用它,它使你的代码在将来更容易修改,并使两个类更容易测试。

(见 http://en.wikipedia.org/wiki/Single_responsibility_principle)

编辑:我没有完全阅读您的 3) 要点,很抱歉用单一责任棒打你;)

可以将方法标记为内部方法,而不是私有方法,然后在 AssemblyInfo.cs 文件中使用 InternalsVisibleTo 属性来允许单元测试程序集访问这些内部方法。例如,我的程序集 OVReadySDK 的 AssemblyInfo 文件中的以下语句:

[assembly: InternalsVisibleTo("OVReadySDKUT, PublicKey=002400000480...5baacad")]

允许我的单元测试程序集 OVReadySDKUT 中的测试方法访问 OVReadySDK 中的类和方法,就好像测试方法是同一个程序集一样。

您可以通过搜索"InternalsVisibleTo 单元测试"来找到该技术的很多示例。请注意,如果程序集已签名,则需要在 InternalsVisibleTo 语句中提供公钥参数。