0

私は職場でプロジェクトを行っており、私の目標の一部は、テストドライブ開発アプローチを学び、適用することです。

EF コードの最初のリポジトリで動作するビジネス クラスを設計していますが、実際にデータベースにアクセスするのではなく、リポジトリのモックを作成したいと考えています。

次のリポジトリ インターフェイスが与えられた場合、MOQ のようなモック フレームワークを使用してこれを達成するにはどうすればよいですか? 課題は、他のエンティティを含めることができる Find メソッドをどのように moq するかということです。

public interface IRepository<T> where T : EntityBase, new()
    {
        ICollection<T> Find(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] fetchSelectors);
        ICollection<T> Find(Expression<Func<T, bool>> predicate, int pageNumber, int size, Expression<Func<T, object>> orderBy, string sortOrder, out int count, params Expression<Func<T, object>>[] fetchSelectors);
        ICollection<T> FindAll(params Expression<Func<T, object>>[] fetchSelectors);
        ICollection<T> FindAll(int pageNumber, int size, Expression<Func<T, object>> orderBy, string sortOrder, out int count, params Expression<Func<T, object>>[] fetchSelectors);
        void Save(T entity);
        void Delete(T entity);
        T Create();
    }

これが私の汎用リポジトリの実装です。

public class GenericRepository<T> : IRepository<T> where T : EntityBase, new()
    {
        public GenericRepository(IDbContext context)
        {
            Guard.ArgumentNotNull(context, "context");
            this.Context = context;
            this.DbSet = this.Context.CreateDbSet<T>();
        }

        protected IDbContext Context
        {
            get;
            set;
        }

        protected System.Data.Entity.IDbSet<T> DbSet
        {
            get;
            set;
        }

        public virtual ICollection<T> Find(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] fetchSelectors)
        {
            return this.BuildQuery(predicate, fetchSelectors).ToList();
        }

        public virtual ICollection<T> FindAll(params Expression<Func<T, object>>[] fetchSelectors)
        {
            return this.BuildQuery(p => true, fetchSelectors).ToList();
        }

        public virtual void Save(T entity)
        {
            Guard.ArgumentNotNull(entity, "entity");
            this.Context.SaveOrUpdate(entity);
        }

        public virtual void Delete(T entity)
        {
            Guard.ArgumentNotNull(entity, "entity");
            this.DbSet.Remove(entity);
        }

        public T Create()
        {
            return this.DbSet.Create();
        }

        private IQueryable<T> BuildQuery(Expression <Func<T, bool>> predicate, params Expression<Func<T, object>>[] fetchSelectors)
        {
            var query = this.DbSet as IQueryable<T>;
            if (fetchSelectors != null)
            {
                foreach (var fetchSelector in fetchSelectors)
                {
                    query = query.Include(fetchSelector);
                }
            }
            return query.Where(predicate);
        }


        public ICollection<T> Find(Expression<Func<T, bool>> predicate, int pageNumber, int size, Expression<Func<T, object>> orderBy, string sortOrder, out int count, params Expression<Func<T, object>>[] fetchSelectors)
        {
            count = (this.DbSet as IQueryable<T>).Count(predicate);

            if (size < 1 || size > count)
            {
                throw new ArgumentOutOfRangeException("size");
            }

            var maxPageNumber = (count + size - 1) / size;
            if (pageNumber < 1 || pageNumber > maxPageNumber)
            {
                throw new ArgumentOutOfRangeException("pageNumber");
            }
            if (sortOrder != "asc" && sortOrder != "desc")
            {
                throw new ArgumentException("sortOrder");
            }
            var skipCount = (pageNumber - 1) * size;
            var query = BuildQuery(predicate, fetchSelectors);
            query = sortOrder == "asc" ? query.OrderBy(orderBy) : query.OrderByDescending(orderBy);
            return query.Skip(skipCount).Take(size).ToList();
        }

        public ICollection<T> FindAll(int pageNumber, int size, Expression<Func<T, object>> orderBy, string sortOrder, out int count, params Expression<Func<T, object>>[] fetchSelectors)
        {
            return Find(p => true, pageNumber, size, orderBy, sortOrder, out count, fetchSelectors);
        }
    }

最後に、IDbContext は次のとおりです。

public interface IDbContext
    {
        void SaveOrUpdate<T>(T entity) where T : EntityBase;
        IDbSet<TEntity> CreateDbSet<TEntity>() where TEntity: EntityBase;
    }
4

1 に答える 1

1

に依存するビジネス ロジック クラスがあるとしますIRepository<T>

public class FooBusinessLogicClass
{
    readonly IRepository<Foo> repository;

    public FooBusinessLogicClass( IRepository<Foo> repository )
    {
        this.repository = repository;
    }

    public ICollection<Foo> FindFoo()
    {
        ...
        var collection = repository.Find( x => x.SomeLambdaExpression(), y => y.SomeOtherExpression() );
        ...

        return collection;
    }
}

リポジトリが呼び出されたときに偽のデータを返すようにしたいだけで、どのパラメータが渡されたかは気にしない場合は、Moq 関数を使用できますIt.IsAny<T>()

    public void Verify()
    {
        //Arrange
        var repositoryMock = new Mock<IRepository<Foo>>();
        var example = new FooBusinessLogicClass( repositoryMock.Object );

        //Our fake data for the repository to return
        var expectedResult = new[] { new Foo(), new Foo() };

        //Our setup that ignores the lambda expressions
        repositoryMock.Setup( mock => mock.Find(
                    It.IsAny<Expression<Func<Foo, bool>>>(),
                    It.IsAny<Expression<Func<Foo, object>>[]>() ) )
                .Returns( expectedResult );

        //Act
        var actualResult = example.FindFoo();

        //Assert
        Assert.AreEqual( expectedResult, actualResult );
    }
}

パラメータが何であるかを気にする場合(それが呼び出されたことを確認したいとしましょうFind)、ラムダ式を に含めるだけですVerify()

        //Assert
        repositoryMock.Verify( mock => mock.Find( 
                x => x.SomeLambdaExpression(), 
                y => y.SomeOtherExpression() ) );

いずれにせよ、 の実装GenericRepository<T>とインターフェースの詳細はIDBContext、ビジネス ロジック クラスのテストには関係ありません。GenericRepository<T>ここで、 ( のモックを使用して)の実装に対して単体テストを書きたいと思うかもしれませんIDBContextが、それは別の問題です。

于 2012-06-06T20:06:10.010 に答える