5

私はビヘイビア駆動の開発を試みていますが、書いているときに自分のデザインを二番目に推測していることに気づきました。これは私の最初のグリーンフィールド プロジェクトであり、私の経験不足によるものかもしれません。とにかく、これは私が書いているクラスの簡単な仕様です。専用の動作駆動型フレームワークを使用する代わりに、BDD スタイルの NUnit で記述されています。これは、プロジェクトが .NET 2.0 をターゲットにしており、すべての BDD フレームワークが .NET 3.5 を採用しているように見えるためです。

[TestFixture]
public class WhenUserAddsAccount
{
    private DynamicMock _mockMainView;
    private IMainView _mainView;

    private DynamicMock _mockAccountService;
    private IAccountService _accountService;

    private DynamicMock _mockAccount;
    private IAccount _account;

    [SetUp]
    public void Setup()
    {
        _mockMainView = new DynamicMock(typeof(IMainView));
        _mainView = (IMainView) _mockMainView.MockInstance;

        _mockAccountService = new DynamicMock(typeof(IAccountService));
        _accountService = (IAccountService) _mockAccountService.MockInstance;

        _mockAccount = new DynamicMock(typeof(IAccount));
        _account = (IAccount)_mockAccount.MockInstance;
    }

    [Test]
    public void ShouldCreateNewAccount()
    {
        _mockAccountService.ExpectAndReturn("Create", _account);
        MainPresenter mainPresenter = new MainPresenter(_mainView, _accountService);
        mainPresenter.AddAccount();
        _mockAccountService.Verify();
    }
}

MainPresenter で使用されるインターフェイスには、まだ実際の実装はありません。AccountService は、新しいアカウントの作成を担当します。個別のプラグインとして定義された IAccount の複数の実装が存在する可能性があります。実行時に複数のアカウントがある場合、ユーザーは作成するアカウントの種類を選択するように求められます。それ以外の場合、AccountService は単にアカウントを作成します。

私が不安に思っていることの 1 つは、1 つの仕様/テストを作成するだけで、いくつのモックが必要になるかということです。これはBDDを使用することの単なる副作用ですか、それとも間違った方法で進んでいますか?

[アップデート]

MainPresenter.AddAccount の現在の実装は次のとおりです。

    public void AddAccount()
    {
        IAccount account;
        if (AccountService.AccountTypes.Count == 1)
        {
            account = AccountService.Create();
        }
        _view.Accounts.Add(account);
    }

ヒント、提案、または代替案を歓迎します。

4

7 に答える 7

3

トップ ツー ダウンの開発を行っている場合、多くのモックを使用していることに気付くのは非常に一般的です。必要なピースはそこにないので、当然それらをモックする必要があります。そうは言っても、これは受け入れレベルのテストのように感じます. 私の経験では、BDD またはコンテキスト/仕様は、単体テスト レベルで少し奇妙になり始めます。単体テストレベルでは、おそらく次のようなことをするでしょう...

when_adding_an_account
   should_use_account_service_to_create_new_account
   should_update_screen_with_new_account_details

IAccount のインターフェイスの使用を再検討することをお勧めします。私は個人的に、サービスのインターフェイスをドメイン エンティティ上に維持することに固執しています。しかし、それは個人的な好みです。

他のいくつかの小さな提案...

  • アサーションに文字列を使用しないようにするため、Rhino Mocks (または Moq) などのモッキング フレームワークの使用を検討することをお勧めします。
  _mockAccountService.Expect(モック => モック.Create())
     .Return(_アカウント);

  • BDD スタイルを実行している場合、私が見た一般的なパターンの 1 つは、テスト セットアップにチェーン クラスを使用することです。あなたの例では...
パブリック クラス MainPresenterSpec
{
    // モックの保護変数

    [設定]
    public void Setup()
    {
       // モックのセットアップ
    }

}

[テストフィクスチャ]
パブリック クラス WhenUserAddsAccount : MainPresenterSpec
{
    [テスト]
    public void ShouldCreateNewAccount()
    {
    }
}
  • また、ガード句を使用するようにコードを変更することをお勧めします..
     public void AddAccount()
     {
        if (AccountService.AccountTypes.Count != 1)
        {
            // ここでやりたいことを実行します。メッセージを投げる?
        戻る;
        }

    IAccount アカウント = AccountService.Create();

        _view.Accounts.Add(アカウント);
     }
于 2009-06-10T19:23:16.633 に答える
2

RhinoAutoMocker ( StructureMapの一部) などの自動モック コンテナーを使用すると、テスト ライフのサポートがはるかに簡単になります。自動モック コンテナーを使用してテスト対象のクラスを作成し、テストに必要な依存関係を要求します。コンテナーはコンストラクターに 20 個の物を注入する必要があるかもしれませんが、1 つだけテストする必要がある場合は、その 1 つを要求するだけで済みます。

using StructureMap.AutoMocking;

namespace Foo.Business.UnitTests
{
    public class MainPresenterTests
    {
        public class When_asked_to_add_an_account
        {
            private IAccountService _accountService;
            private IAccount _account;
            private MainPresenter _mainPresenter;

            [SetUp]
            public void BeforeEachTest()
            {
                var mocker = new RhinoAutoMocker<MainPresenter>();
                _mainPresenter = mocker.ClassUnderTest;
                _accountService = mocker.Get<IAccountService>();
                _account = MockRepository.GenerateStub<IAccount>();
            }

            [TearDown]
            public void AfterEachTest()
            {
                _accountService.VerifyAllExpectations();
            }

            [Test]
            public void Should_use_the_AccountService_to_create_an_account()
            {
                _accountService.Expect(x => x.Create()).Return(_account);
                _mainPresenter.AddAccount();
            }
        }
    }
}

構造的には、スキャンしやすいので、RunningThemAllTogether ではなく単語間にアンダースコアを使用することを好みます。また、テスト対象のクラスの名前が付けられた外部クラスと、テスト対象のメソッドの名前が付けられた複数の内部クラスも作成します。テスト メソッドを使用すると、テスト対象のメソッドの動作を指定できます。NUnit で実行すると、次のようなコンテキストが得られます。

Foo.Business.UnitTests.MainPresenterTest
  When_asked_to_add_an_account
    Should_use_the_AccountService_to_create_an_account
    Should_add_the_Account_to_the_View
于 2009-06-10T22:06:35.290 に答える
1

はい、あなたのデザインには欠陥があります。あなたはモックを使用しています:)

もっと真剣に、私はあなたのデザインが層にされるべきであると提案している前のポスターに同意します。そうすれば、各層は別々にテストされることができます。原則として、テストコードが実際の本番コードを変更する必要があるのは間違っていると思います。これは、デバッグまたはリリース用にコードをコンパイルする方法を自動的かつ透過的に実行できない限りです。

これは、ハイゼンベルグの不確定性原理のようなものです。モックを入れると、コードが変更されてメンテナンスの問題になり、モック自体がバグを導入またはマスクする可能性があります。

クリーンなインターフェースをお持ちの場合、別のモジュールへの実装されていないインターフェースをシミュレート(またはモック)する単純なインターフェースを実装することに異論はありません。このシミュレーションは、単体テストなどで、モックと同じように使用できます。

于 2009-06-10T23:39:50.017 に答える
1

これは、アカウントを返すサービスを提供するプレゼンターにとって正しいモックの数のようです。

ただし、これは単体テストというよりも受け入れテストのように思えます。おそらく、アサーションの複雑さを軽減すると、嘲笑される懸念のセットが少なくなることに気付くでしょう。

于 2009-06-10T15:49:38.767 に答える
0

モックが必要な場合は、デザインが間違っていると思います。

コンポーネントは階層化する必要があります。コンポーネントAを個別に構築してテストします。次に、B+Aをビルドしてテストします。満足したら、レイヤーCを作成し、C + B+Aをテストします。

あなたの場合、「_mockAccountService」は必要ありません。実際のAccountServiceがテストされている場合は、それを使用してください。そうすれば、バグがMainPresentorにあり、モック自体にはないことがわかります。

実際のAccountServiceがテストされていない場合は、停止してください。戻って、正しく機能していることを確認するために必要なことを実行します。あなたが本当にそれに頼ることができるところまでそれを手に入れてください、そうすればあなたはモックを必要としません。

于 2009-06-10T23:20:43.240 に答える
0

プレゼンターの作成中に、すべてのモック管理を取り除くためにMockContainersを使用したい場合があります。単体テストを大幅に簡素化します。

于 2009-06-10T19:32:05.867 に答える
0

これは問題ありませんが、IoC 自動モック コンテナがどこかにあると思います。コードは、テスト ライターがテストでモック オブジェクトと実際のオブジェクトを手動で (明示的に) 切り替えることを暗示しています他のすべてのクラスとモックを使用します。

私が言おうとしているのは、 と の両方を使用するテスト クラスがある場合mainViewmockMainView厳密な意味での単体テストではなく、統合テストのようなものだということです。

于 2009-06-10T19:33:05.053 に答える