2

これは貧血ドメイン モデルでした。

public partial class Person
{
    public virtual int PersonId { get; internal protected set; }
    public virtual string Title { get; internal protected set; } 
    public virtual string FirstName { get; internal protected set; } 
    public virtual string MiddleName { get; internal protected set; } 
    public virtual string LastName { get; internal protected set; } 
}

そして、これはその動作です:

public static class Services
{

    public static void UpdatePerson(Person p, string firstName, string lastName)
    {
        // validate  firstname and lastname
        // if there's a curse word, throw an exception


        // if valid, continue

        p.FirstName = firstName;
        p.LastName = lastName;


        p.ModifiedDate = DateTime.Now;
    }

}

そして、それはかなりテスト可能です:

[TestMethod]

public void Is_Person_ModifiedDate_If_Updated()
{
    // Arrange
    var p = new Mock<Person>();

    // Act 
    Services.UpdatePerson(p.Object, "John", "Lennon");

    // Assert            
    p.VerifySet(x => x.ModifiedDate = It.IsAny<DateTime>());
}

ただし、データと動作がより論理的にまとまるリッチ ドメイン モデルを実践したかったのです。したがって、上記のコードは次のように変換されます。

public partial class Person
{
    public virtual int PersonId { get; internal protected set; }
    public virtual string Title { get; internal protected set; }
    public virtual string FirstName { get; internal protected set; } 
    public virtual string MiddleName { get; internal protected set; }
    public virtual string LastName { get; internal protected set; } 

    public virtual void UpdatePerson(string firstName, string lastName)
    {
        // validate  firstname and lastname
        // if there's a curse word, throw an exception


        // if valid, continue


        this.FirstName = firstName;
        this.LastName = lastName;

        this.ModifiedDate = DateTime.Now;
    }           
}

ただし、テストの問題が発生します。

[TestMethod]
public void Is_Person_ModifiedDate_If_Updated()
{
    // Arrange
    var p = new Mock<Person>();

    // Act 
    p.Object.UpdatePerson("John", "Lennon");

    // Assert            
    p.VerifySet(x => x.ModifiedDate = It.IsAny<DateTime>());
}

単体テスト エラー:

Result Message: 

Test method Is_Person_ModifiedDate_If_Updated threw exception: 
Moq.MockException: 
Expected invocation on the mock at least once, but was never performed: x => x.ModifiedDate = It.IsAny<DateTime>()
No setups configured.

Performed invocations:
Person.UpdatePerson("John", "Lennon")
Result StackTrace:  
at Moq.Mock.ThrowVerifyException(MethodCall expected, IEnumerable`1 setups, IEnumerable`1 actualCalls, Expression expression, Times times, Int32 callCount)
   at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times)
   at Moq.Mock.VerifySet[T](Mock`1 mock, Action`1 setterExpression, Times times, String failMessage)
   at Moq.Mock`1.VerifySet(Action`1 setterExpression)
   at Is_Person_ModifiedDate_If_Updated()

モックされたオブジェクトからメソッドを直接呼び出すと、モックされたオブジェクトは、そのプロパティまたはメソッドのいずれかが呼び出されたかどうかを検出できません。リッチ ドメイン モデルを単体テストする適切な方法は何ですか?

4

2 に答える 2

2

あなたの電話:

p.Object.UpdatePerson("John", "Lennon");

モックでパブリックvirtualメソッドを呼び出します。UpdatePersonモックには動作Loose( とも呼ばれます) があり、その仮想メソッドDefaultはありませんでした。Setup

その場合の Moq の動作は、 の実装 (オーバーライド) で何もUpdatePersonしないことです。

これを変更する方法はいくつかあります。

  • メソッドvirtualからキーワードを削除できます。UpdatePersonその場合、Moq はその動作をオーバーライドしません (できません)。
  • またはSetup、呼び出す前に実際に Moq を使用して仮想メソッドを作成することもできます。(実際にテストしたいメソッドをオーバーライドするため、この場合は役に立ちません。)
  • または、 p.CallBase = true;と言うことができます。メソッドを呼び出す前に。これは次のように機能します (Loose動作あり):セットアップされていないメンバーが呼び出された場合、Moq は基本クラスの実装を呼び出しますvirtual

これはあなたが見たものを説明しています。セルゲイ・ベレゾフスキーが回答で与えたアドバイスに同意できます。

于 2014-01-01T10:58:49.897 に答える