3

実際にコードを書いた後でテストを書くのはあまり良くないことを私は知っています。私は初心者のユニットテストを行っており、ユニットテストは多くの優れた利点をもたらす可能性があると感じているため、可能な限りカバーするというアイデアに夢中になっています。

たとえば、次のコードを考えてみましょう。

public class ProjectsPresenter : IProjectsViewObserver
{
    private readonly IProjectsView _view;
    private readonly IProjectsRepository _repository;

    public ProjectsPresenter(IProjectsRepository repository, IProjectsView view)
    {
        _view = view;
        _repository = repository;
        Start();
    }

    public void Start()
    {
        _view.projects = _repository.FetchAll();
        _view.AttachPresenter(this);
    }

}

それで、上記のコードを見て、上記のコードのその部分に通常どのようなテストを書くべきか答えていただけますか?

コンストラクターで書き込みテストを実行して、リポジトリのFetchAllが呼び出され、ビューサイトでAttachPresenterが呼び出されていることを確認しています。


ポストエディット

これが私のビューインターフェイスです:

public interface IProjectsView
{
    List<Project> projects { set; }
    Project project { set; }

    void AttachPresenter(IProjectsViewObserver projectsPresenter);
}

ビューは次のとおりです。

public partial class ProjectsForm : DockContent, IProjectsView
{
    private IProjectsViewObserver _presenter;
    public ProjectsForm()
    {
        InitializeComponent();
    }

    public Project project
    {
        set
        {
            listBoxProjects.SelectedItem = value;
        }
    }

    public List<Project> projects
    {
        set
        {
            listBoxProjects.Items.Clear();   
            if ((value != null) && (value.Count() > 0))
                listBoxProjects.Items.AddRange(value.ToArray());
        }
    }

    public void AttachPresenter(IProjectsViewObserver projectsPresenter)
    {
        if (projectsPresenter == null)
            throw new ArgumentNullException("projectsPresenter");

        _presenter = projectsPresenter;
    }

    private void listBoxProjects_SelectedValueChanged(object sender, EventArgs e)
    {
        if (_presenter != null)
            _presenter.SelectedProjectChanged((Project)listBoxProjects.SelectedItem);
    }
}

投稿編集#2

これが私がリポジトリとの相互作用をテストする方法です。すべて大丈夫ですか?

    [Test]
    public void ProjectsPresenter_RegularProjectsProcessing_ViewProjectsAreSetCorrectly()
    {
        // Arrange
        MockRepository mocks = new MockRepository();
        var view = mocks.StrictMock<IProjectsView>();
        var repository = mocks.StrictMock<IProjectsRepository>();
        List<Project> projList = new List<Project> {
            new Project { ID = 1, Name = "test1", CreateTimestamp = DateTime.Now },
            new Project { ID = 2, Name = "test2", CreateTimestamp = DateTime.Now }
        };
        Expect.Call(repository.FetchAll()).Return(projList);
        Expect.Call(view.projects = projList);
        Expect.Call(delegate { view.AttachPresenter(null); }).IgnoreArguments();
        mocks.ReplayAll();
        // Act
        ProjectsPresenter presenter = new ProjectsPresenter(repository, view);
        // Assert
        mocks.VerifyAll();            
    }
4

5 に答える 5

2

実際にコードを書いた後にテストを書くのはあまり良くないことはわかっています

テストをまったく書かないよりはましです。

メソッドは 2 つの外部コンポーネントで動作し、その相互作用を検証する必要があります (前述の引数の検証に加えて)。FetchAll呼び出されたかどうかを確認しても値が得られません(または、それを確認すると何かProjectsRepositoryが返されます - これはテスト自体に属します) - ビューのプロジェクトが設定されていることを確認します (FetchAll呼び出されたかどうかを間接的に確認します)。必要なテストは次のとおりです。

  • ビュー プロジェクトが期待値に設定されていることを確認する
  • プレゼンターが接続されていることを確認する
  • 入力引数の検証

編集:最初のケースをテストする方法の例(プロジェクトが設定されています)

// "RegularProcessing" in test name feels a bit forced;
// in such cases, you can simply skip 'conditions' part of test name
public void ProjectsPresenter_SetsViewProjectsCorrectly()
{
    var view = MockRepository.GenerateMock<IProjectView>();
    var repository = MockRepository.GenerateMock<IProjectsRepository>();
    // Don't even need content;
    // reference comparison will be enough
    List<Project> projects = new List<Project>();
    // We use repository in stub mode;
    // it will simply provide data and that's all
    repository.Stub(r => r.FetchAll()).Return(projects);
    view.Expect(v => v.projects = projects);

    ProjectsPresenter presenter = new ProjectsPresenter(repository, view);

    view.VerifyAllExpecations();
}

AttachPresenter2 番目のケースでは、ビューが有効なオブジェクトで呼び出されるという期待を設定します。

public void ProjectsPresenter_AttachesPresenterToView()
{
    // Arrange
    var view = MockRepository.GenerateMock<IProjectView>();
    view.Expect(v => v.AttachPresenter(Arg<IProjectsViewObserver>.Is.Anything));
    var repository = MockRepository.GenerateMock<IProjectsRepository>();

    // Act
    var presenter = new ProjectsPresenter(repository, view);

    // Assert
    view.VerifyAllExpectations();
}
于 2012-05-01T07:36:13.397 に答える
0

開始時に次のような簡単なテストを追加します。

  • null参照チェック

  • FetchAll()任意の値を返します

最初は多くのテストコードを追加しないでください。ただし、開発コードが変更されたら、後でテストコードを改良してください。

于 2012-05-01T06:14:45.000 に答える
0

ArgumentException、コーナーケース、FetchAll() の通常のケースなど、例外のテストを追加します。

PS。start は公開する必要がありますか?

于 2012-05-01T06:19:22.373 に答える
0

Pex は、チェックする価値のある興味深いツールです。コード カバレッジの高い一連の単体テストを生成できます: http://research.microsoft.com/en-us/projects/pex/。それは、自分のコードに関する自分自身の知識に取って代わるものではありません。また、どのテスト シナリオが他のシナリオよりも重要であるかということもありますが、それに対する素晴らしい追加です。

于 2012-05-01T07:44:39.400 に答える
0

本番コードを書く前にテストを書く目的は、何よりもまず、あなた (開発者) を「自分のコードがいつ機能するかをどうやって知るか?」という考え方にすることです。コード自体ではなく、実際に動作するコードの結果がどのようなものになるかに開発の焦点が当てられている場合、無関係な懸念ではなく、コードによって得られる実際のビジネス価値に焦点が当てられます (数百万の工数がユーザーの機能の構築と維持に費やされてきました)。求められたり、望まれたり、必要とされたりしたことはありません)。これを行うと、「テスト駆動開発」を行うことになります。

純粋な TDD を行っている場合、答えは 100% のコード カバレッジです。つまり、単体テスト コードの行でまだカバーされていない製品コードを 1 行も記述しないということです。

Visual Studio で Test->Analyze Code Coverage に移動すると、カバーしていないすべてのコード行が表示されます。

実際には、100% のコード カバレッジを強制することは必ずしも実現可能ではありません。また、他のコードよりもはるかに重要なコード行がいくつかあります。どちらを決定するかは、各回線によって提供されるビジネス価値と、その回線が失敗した場合の結果に再び依存します。一部の行 (ロギングなど) は、他の行よりも結果が小さい場合があります。

于 2017-03-20T20:55:59.413 に答える