当单元测试时,在这个例子中使用验证来间接测试一个方法吗?

use*_*438 3 c# unit-testing mocking

我想在下面的代码中测试FindPolicyToBeResent().我有几个选项,但是如果我的方法即最后一个选项没问题,我想知道其他人如何处理这种情况?

  • 使FindPolicyToBeResent()公开.这不是一个选项,因为它公开了一个实现的唯一原因是测试并使接口混乱
  • 仅使用公共API进行单元测试但这可能很困难,因为我从未公开我在return语句中直接过滤的集合,并且出于安全原因不能这样做.这意味着我只能进行有限的测试
  • 通常我会在这种情况下将代码分解为一个新对象,但在这种情况下感觉不对,我无法预见这个过滤器会在系统中的任何其他地方重复使用,所以它不会比将该方法公之于众任何人都用单一的责任棒打击我(这是合理的),编码是一种平衡的行为,我觉得它会违反Keep It Simple原则.它只是让一个班级服务于测试而不是实际上具有单独的单一响应性.另外,它会导致文件和类膨胀.
  • 我可以使代码成为IEnumerable的扩展方法,这将使它成为可测试的但是我再也不能预测这个过滤器在其他地方被使用,所以如果它保留在这个类中会更有意义
  • 最后一个选项并且更喜欢但可能被视为一个黑客攻击是使用verify()在代码中进一步测试documentResenderRepository.CanPolicyBeResent(policy.Id)的模拟,以查看它被击中的时间.我不确定这是不是一个好主意?思考?

我的偏好是最后一个选项,但确实感觉有点脏,我在底部有一个例子

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
}
Run Code Online (Sandbox Code Playgroud)

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

[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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*Dan 5

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

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

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

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