7

「サービスレイヤー」/「アプリケーションファサードレイヤー」メソッドの単体テストを試みています。これは私が単体テストしようとしている方法です:

// Create a new order in the database for a customer.  Given a customer id,
// will create a new order and return an OrderDto for use in the presentation
// layer.
public OrderDto CreateOrderForCustomer(int customerId)
{
  // Find the customer
  var customer = _customerRepository.GetCustomerById(customerId);

  // Create an order and apply special logic to get it ready for use.
  var orderFactory = new OrderFactory();
  var order = orderFactory.CreateOrder(customer);

  // IMPORTANT: This is what I'm trying to unit test ...
  _orderRepository.Save(order);

  order.Status = "Editing";

  // Using AutoMapper to turn this into a DTO that will be returned
  // to the Presentation layer.  The Mappings are created in the 
  // constructor and not depicted in this code snippet.
  var orderDto = Mapper.Map<Order, OrderDto>(order);

  return orderDto;
}

(注...わかりやすくするために、ここに大量のメモを追加しました。通常、私はそれほどおしゃべりではありません。)

このメソッドの仕事は、ドメイン レイヤー メソッドとパーシスタンス レイヤー メソッドを調整して空の注文を作成し、それを永続化し、単純な DTO として返すことなので、これは FakeItEasy にとって素晴らしい仕事だと思いました...それらを確認します重要なメソッドは、FakeItEasy の MustHaveHappened() を使用して確実に呼び出されるように適切に編成されています。

それを念頭に置いて、私が作成した単体テストは次のとおりです。

[TestMethod]
public void CreateOrderForCustomer_ValidCustomer_CreatesNewOrder()
{
  // Arrange
  var customer = _customerRepository.GetCustomerById(1);
  Assert.AreEqual(0, customer.Orders.Count);

  // Act
  var orderDto = _orderEntryService.CreateOrderForCustomer(1);

  // Assert

  // Here I'm trying to make sure to re-create the order that was actually 
  // sent into the _customerRepository.Save() ... I should be able to
  // simple un-map the OrderDto back to an Order, and undo the property 
  // change.
  var order = Mapper.Map<OrderDto, Order>(orderDto);
  order.Status = "New";

  A.CallTo(() => _customerRepository.GetCustomerById(1)).MustHaveHappened();

  // **THIS CAUSES AN EXCEPTION**
  A.CallTo(() => _orderRepository.Save(order)).MustHaveHappened();
  Assert.AreEqual(1, customer.Orders.Count);
}

単体テストでは、テスト中のメソッドで作成された実際の注文にアクセスできません。次善の策を試してみました...テスト中のメソッドによって返された注文の DTO バージョンを取得し、マップしますOrder の DTO バージョンをドメイン モデル Order の新しいインスタンスに戻し、FakeItEasy の MustHaveHappened() に送信する前にプロパティが同じであることを確認します。

単体テストをデバッグし、実際の注文のプロパティと FAKED の注文のプロパティを比較しました...確かに、それらは同一です。また、デバッグを通じて、_customerRepository.Save(order) が実際に呼び出されていることを確認できます。

質問 .MustHaveHappened() が失敗するのは、本質的に Order オブジェクトの 2 つの異なるインスタンスを送信しているためです。それらのプロパティは同じですが? プロパティは同じですが、FakeItEasy は、メソッド呼び出しが行われたことを確認するために入力パラメーターの同じインスタンスを必要としますか?

さらに、この種のものをどのようにテストする必要があるかについての提案 (つまり、オーケストレーション/サービス/「アプリケーション ファサード」/レイヤー メソッドと呼びたいもの) はありますか?

4

1 に答える 1

18

.MustHaveHappened() は、基本的に Order オブジェクトの 2 つの異なるインスタンスを送信しているため、それらのプロパティは同じですが、失敗していますか?

はい。FakeItEasy は を使用します.Equals。これは (クラスがそれをオーバーライドしない限り) 参照型のデフォルトで参照の等価性になります。

(...) FakeItEasy は、メソッド呼び出しが行われたことを確認するために入力パラメーターの同じインスタンスを必要としますか?

いいえ。次のようにカスタム引数の一致を行うことができます。

A.CallTo(() => _orderRepository.Save(A<Order>.That.Matches(o =>
    o.Status == "New" &&
    o.Id == 10
))).MustHaveHappened();

しかし、この問題により、コードの問題が明らかになりました。_customerRepositoryサンプルから、依存関係として注入していることは明らかです。それは素晴らしいことです。で同じことをしてみませんOrderFactoryか?インターフェイス/基本クラスの依存関係を介して注入された場合、簡単にモック (偽造) することができ、現在の問題は存在しません。

コードを変更できる場合は、ファクトリを注入することをお勧めします (単純なガイドラインに従ってください - 「no news is good news!」 )。そうでない場合は、上記のサンプルで行ったように、カスタム マッチャーを使用して注文のプロパティを確認します。

于 2012-12-18T17:50:21.060 に答える