web-dev-qa-db-ja.com

Entity FrameworkとMoqを使用して単体テストを行うにはどうすればよいですか?

私はMoqを初めて使用し、データのバッキングストアのように使用したいと考えていますが、ライブデータベースには触れません。

私の設定は次のとおりです。

  • UnitOfWorkにはすべてのリポジトリが含まれており、アプリケーション全体のデータアクセスに使用されます。
  • リポジトリは、DbContextによって提供されるDbSetへの直接フ​​ックを表します。
  • DbContextには、すべてのDbSetが含まれます。

これが私のこれまでのテストです:

        // ARRANGE
        var user = new User()
        {
            FirstName = "Some",
            LastName = "Guy",
            EmailAddress = "[email protected]",
        };

        var mockSet = new MockDbSet<User>();
        var mockContext = new Mock<WebAPIDbContext>();

        mockContext.Setup(c => c.Set<User>()).Returns(mockSet.Object);

        // ACT
        using (var uow = UnitOfWork.Create(mockContext.Object))
        {
            uow.UserRepository.Add(user);
            uow.SaveChanges();
        }

        // ASSERT
        mockSet.Verify(u => u.Add(It.IsAny<User>()), Times.Once());

ユーザーがモックDbSetに追加されたことを確認できるため、私のテストは成功したようですが、実際にそのデータを取得してさらにアサーションを実行する必要があります(これは単なるアドホックテストです)。

テストフレームワークが頭を悩ませていることをアドバイスしてください。また、他のテストフレームワークが使いやすい場合は、他のテストフレームワークに移行することもできます。

ありがとうございました。

更新:これが私の作業コードです。

ユニットテスト

        // ARRANGE
        var user = new User()
        {
            FirstName = "Some",
            LastName = "Guy",
            EmailAddress = "[email protected]",
        };

        var mockSet = new MockDbSet<User>();
        var mockContext = new Mock<WebAPIDbContext>();

        mockContext.Setup(c => c.Set<User>()).Returns(mockSet.Object);

        // ACT
        using (var uow = UnitOfWork.Create(mockContext.Object))
        {
            uow.UserRepository.Add(user);
            uow.SaveChanges();
        }

        // ASSERT
        mockSet.Verify(u => u.Add(It.IsAny<User>()), Times.Once());

        // TODO: Further assertations can now take place by accessing mockSet.BackingStore.
    }

MockDbSet

class MockDbSet<TEntity> : Mock<DbSet<TEntity>> where TEntity : class
{
    public ICollection<TEntity> BackingStore { get; set; }

    public MockDbSet()
    {
        var queryable = (this.BackingStore ?? (this.BackingStore = new List<TEntity>())).AsQueryable();

        this.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(queryable.Provider);
        this.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(queryable.Expression);
        this.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(queryable.ElementType);
        this.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(() => queryable.GetEnumerator());

        // Mock the insertion of entities
        this.Setup(e => e.Add(It.IsAny<TEntity>())).Returns((TEntity entity) =>
        {
            this.BackingStore.Add(entity);

            return entity;
        });

        // TODO: Other DbSet members can be mocked, such as Remove().
    }
}
7
Rhonage

バッキングストアとして機能するコレクションを作成し、バッキングコレクションで設定された列挙データベースをモックする必要があります。

public class MockDbSet<TEntity> : Mock<DbSet<TEntity>> where TEntity : class {
    public MockDbSet(List<TEntity> dataSource = null) {
        var data = (dataSource ?? new List<TEntity>());
        var queryable = data.AsQueryable();

        this.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(queryable.Provider);
        this.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(queryable.Expression);
        this.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(queryable.ElementType);
        this.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(() => queryable.GetEnumerator());
        //Mocking the insertion of entities
        this.Setup(_ => _.Add(It.IsAny<TEntity>())).Returns((TEntity arg) => {
            data.Add(arg);
            return arg;
        });

        //...the same can be done for other members like Remove
    }
}

これで、リストを使用してデータを保持できます

// ARRANGE
var dataSource = new List<User>(); //<-- this will hold data
var user = new User()
{
    FirstName = "Some",
    LastName = "Guy",
    EmailAddress = "[email protected]",
};

var mockSet = new MockDbSet<User>(dataSource);
var mockContext = new Mock<WebAPIDbContext>();

mockContext.Setup(c => c.Set<User>()).Returns(mockSet.Object);

// ACT
using (var uow = UnitOfWork.Create(mockContext.Object))
{
    uow.UserRepository.Add(user);
    uow.SaveChanges();


    // ASSERT
    mockSet.Verify(u => u.Add(It.IsAny<User>()), Times.Once());
    Assert.IsTrue(dataSource.Contains(user)); //<-- shows mock actually added item
    Assert.IsTrue(uow.UserRepository.Any(u => u == user)); //<-- show you can actually query mock DbSet
}
10
Nkosi