3

次のコードでFindPolicyToBeResent()をテストしたいと思います。利用できるオプションがいくつかありますが、私のアプローチ、つまり最後のオプションで問題がない場合にも、他の誰かがこの状況にどのようにアプローチするか知りたいですか?

  • FindPolicyToBeResent()をパブリックにします。これは、テストの唯一の理由で実装を公開し、インターフェイスを混乱させるため、オプションではありません
  • パブリックAPIのみを使用した単体テストですが、returnステートメントで直接フィルタリングしているコレクションを公開することはなく、セキュリティ上の理由から公開できないため、これは難しい場合があります。これは、限られたテストしかできないことを意味します
  • 通常、この状況ではコードを新しいオブジェクトに分割しますが、この場合は正しくないと感じます。このフィルターがシステム内の他の場所で再利用されることは予測できないため、メソッドを公開するよりも前に公開するよりも良い方法はありません。誰もが単一責任スティック(正当化される)で私を殴ります。コーディングはバランスをとる行為であり、KeepItSimpleの原則を破るような気がします。それは、実際に個別の単一の責任を持っているのではなく、テストを提供するためのクラスを作っているような気がします。さらに、ファイルとクラスが肥大化します。
  • コードを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
}

これが最後のオプションの単体テストソリューションですが

[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());
        }
    }
}
4

2 に答える 2

5

パブリック メソッドを介してプライベート メソッドをテストすることは問題ありません。コードが十分にモジュール化されている場合、プライベート メソッドを取得するための条件を正しく設定するために、セットアップ コードが多すぎてはなりません。プライベート メソッドにアクセスするためだけに多くのものを設定していることに気付いた場合は、1 つのクラスで多くのことを行っている可能性があります。

あなたの場合、私はあなたのポイント3)に行き、 PolicyFinder : IPolicyFinder クラスを作成したくなるでしょう。将来的にコードを変更しやすくなり、両方のクラスをテストしやすくなるので、今すぐ再利用する必要はないかもしれません。

( http://en.wikipedia.org/wiki/Single_responsibility_principleを参照)

編集: 私はあなたの 3) の箇条書きを完全には読んでいませんでした。

于 2013-02-04T15:23:57.583 に答える
0

メソッドをプライベートではなく内部としてマークし、AssemblyInfo.cs ファイルでInternalsVisibleTo属性を使用して、単体テスト アセンブリがそれらの内部メソッドにアクセスできるようにすることができます。たとえば、アセンブリ OVReadySDK の AssemblyInfo ファイル内の次のステートメントは次のとおりです。

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

テスト メソッドが同じアセンブリであるかのように、単体テスト アセンブリ OVReadySDKUT のテスト メソッドが OVReadySDK のクラスとメソッドにアクセスできるようにします。

「InternalsVisibleTo unit test」で検索すると、その手法の例がかなり見つかります。InternalsVisibleToアセンブリが署名されている場合は、ステートメントで公開キー引数を指定する必要があることに注意してください。

于 2013-02-04T16:15:59.653 に答える