9

MoqEntity Framework Code First クラスのテストを作成するために使用しようとしています。私は Moq とモッキング手法に非常に慣れていないため、以下で説明するテストを簡単に実行できるかどうか疑問に思っています。いくつかの解決策を Web で検索しましたが、ほとんどは避けたいリポジトリ パターンに基づいています。

ITestEntitiesコンテキスト用のインターフェースがあります

public interface ITestEntities
{
    IDbSet<Order> Orders { get; }
    IDbSet<Product> Products { get; }
    IDbSet<User> Users { get; }
}

次にコンテキスト

public class TestEntities : DbContext, ITestEntities
{
    public TestEntities() : base("name=TestEntities")
    {

    }

    public virtual IDbSet<Order> Orders { get; set; }
    public virtual IDbSet<Product> Products { get; set; }
    public virtual IDbSet<User> Users { get; set; }
}

テストするコントローラーとアクション

public class HomeController : Controller
{
    private ITestEntities db;

    public HomeController()
    {
        db = new TestEntities();
    }

    public HomeController(ITestEntities db)
    {
        this.db = db;
    }

    public ActionResult Index()
    {
        var count = db.Users.Count();
        ViewBag.count = count;

        return View(count);
    }        
}

最後に、Moq を使用した NUnit テスト

[Test]
public void ModelValueShouldBeTwo()
{
    var mockUsers = new Mock<IDbSet<User>>();
    mockUsers.Setup(m => m.Count()).Returns(2);

    var mockDB = new Mock<ITestEntities>();
    mockDB.Setup(db => db.Users).Returns((IDbSet<User>)mockUsers);

    var controller = new HomeController((ITestEntities)mockDB);

    var view = controller.Index();

    Assert.IsInstanceOf<ViewResult>(view);
    Assert.AreEqual(((ViewResult)view).Model, 2);
}

問題は次の行にあります: mockUsers.Setup(m => m.Count()).Returns(2);. このテストを実行すると、次のエラーが発生します。

System.NotSupportedException : Expression references a method that does not belong to the mocked object: m => m.Count<User>()

これは.Count()静的メソッドであるため、Moq でモックできないためだと思います。本格的なリポジトリ パターンを使用せずに Moq を使用してこの単純なアクションをテストする方法はあり.Count()ますか? これは非常にシンプルで、EF Code First で可能であるという印象を持っているからです。

4

2 に答える 2

16

テストエンティティをモックしている場合、チェーンをさらにモックする必要はありません

このようなことを行う必要があります (ただし、私は IDE を使用していないため、微調整が必​​要になる場合があります)

新しい InMemoryDbSet を含めるための更新

[Test]
public void ModelValueShouldBeTwo()
{
    //Build test users
    var mockUsers = new InMemoryDbSet<User>(){ new User(), new User()};
    var mockDB = new Mock<ITestEntities>();
    //Set up mock entities to returntest users.
    mockDB.Setup(db => db.Users).Returns(mockUsers);

    var controller = new HomeController((ITestEntities)mockDB);

    var view = controller.Index();

    Assert.IsInstanceOf<ViewResult>(view);
    Assert.AreEqual(((ViewResult)view).Model, 2);
}

これは、拡張メソッドが、提供したテスト データから単純に機能することを意味します。

dbset のモックに関する優れた記事については、以下を参照して ください http://geekswithblogs.net/Aligned/archive/2012/12/12/mocking-or-faking-dbset.aspx

于 2013-04-25T12:32:31.377 に答える
1

GetEnumerator()代わりにモックCount()

Count()IEnumerable<T>およびをIDbSet<T>実装するオブジェクトの拡張メソッドです。IEnumerable<T>

拡張メソッドには、呼び出されたオブジェクトが渡されます。この場合、署名は次のとおりです。

public static int Count<TSource>(
    this IEnumerable<TSource> source,  //This is your IDbSet that you are mocking
    Func<TSource, bool> predicate
)

Count()特定の値を返すように設定しようとするのではなくIEnumerable<T>、同じ結果が得られるように のメンバーを設定できます。あなたがしなければならないのは、2つの値を列挙するを返すようにIEnumerable<T>設定することだけです。GetEnumerator()Enumerator<T>

このような状況では、通常Enumerator<T>、いくつかのアイテムを含む新しいリストを作成し、それを呼び出すことで作成しGetEnumerator()ます。

mockUsers.Setup(m => m.GetEnumerator()).Returns(new List<Users> {
    new User(),
    new User()
}.GetEnumerator());

もちろん、これはCount()テストで達成しようとしていることに加えて、拡張メソッドを効果的にテストしますが、拡張メソッドが .NET の一部である場合、リスクはかなり低くなります。独自の拡張メソッドを使用および作成しています。

于 2013-04-25T13:30:25.160 に答える