我必须使用Moq设置公开和虚拟方法

时间:2015-06-18 08:53:33

标签: c# unit-testing moq

我有一个存储库,其中包含一些私有方法,以帮助处理需要在该存储库中完成的一些常规内容(不要觉得您需要阅读所有代码) :

public class EFBlogRepository : EFGenericRepository<Blog>, IBlogRepository
{
    public EFBlogRepository( EFDbContext context )
        : base( context )
    {
        this.context = context;
    }

    // Problematic method
    private IQueryable<Blog> PrepareAllBlogsQuery( int page, int amount, string sort, string order, ISearchCriteria searchCriteria )
    {
        var query = context.Blogs
            .Where( x => x.DeletedAt == null );

        ...

        return query;
    }

    public IEnumerable<Blog> GetAllBlogs( int page, int amount, string sort, string order, ISearchCriteria searchCriteria )
    {
        return this.PrepareAllBlogsQuery( page, amount, sort, order, searchCriteria )
            .Skip( ( page - 1 ) * amount )
            .Take( amount );
    }

    public int CountAllBlogs( int page, int amount, string sort, string order, ISearchCriteria searchCriteria )
    {
        return this.PrepareAllBlogsQuery( page, amount, sort, order, searchCriteria )
            .Count();
    }

当我尝试进行单元测试时,问题出现了......

我必须制作PrepareAllBlogsQuery publicvirtual才能使其正常工作(您只需阅读注释位):

// Arrange
DateTime now = DateTime.Now;

var mockDbContext = new Mock<EFDbContext>();
var blogRepository = new Mock<EFBlogRepository>(mockDbContext.Object);

IDbSet<Blog> blogDbSet = new FakeDbSet<Blog>();

List<Blog> blogs = new List<Blog> {                 
    new Blog { BlogID = 1, Description = "1", Status = true, PublishDate = now },
    new Blog { BlogID = 2, Description = "2", Status = true, PublishDate = now },
    new Blog { BlogID = 3, Description = "3", Status = true, PublishDate = now },
    new Blog { BlogID = 4, Description = "4", Status = true, PublishDate = now },
    new Blog { BlogID = 5, Description = "5", Status = true, PublishDate = now },
    new Blog { BlogID = 6, Description = "6", Status = true, PublishDate = now },
};

IQueryable<Blog> blogsQueryable = blogs.AsQueryable();

mockDbContext.SetupGet(c => c.Blogs).Returns(blogDbSet);

// This Setup requires my method to be public and virtual :(
blogRepository.SetupGet(c => c.PrepareAllBlogsQuery(2, 2, SortDirection.DESC, null, null)).Returns(blogsQueryable);                 
// Act
List<Blog> result = blogRepository.Object.GetAllBlogs(2, 2, SortDirection.DESC, null, null).ToList();

// Assert     
Assert.AreEqual("3", result[0].Description);

有什么方法吗?

我甚至不想测试PrepareAllBlogsQuery方法,我只需要模拟框架就可以知道该方法返回的内容,无论其内容如何。

2 个答案:

答案 0 :(得分:1)

是的,您应该为您的存储库创建一个接口,这是您在单元测试中模拟的东西:

public interface IEFBlogRepository
{
    IEnumerable<Blog> GetAllBlogs( int page, int amount, string sort, string order, ISearchCriteria searchCriteria )
}

public class EFBlogRepository : IEFBlogRepository
{
   ...
}

然后在您的单元测试中,您可以模拟IEFBlogRepository并且您不需要去EF附近的任何地方。

var mockRepository = new Mock<IEFBlogRepository>();
mockRepository.Setup(r => r.....);

var thing = new Thing(mockRepository.Object);
thing.DoSomeStuffWhichCallsRepository();

<强>更新

由于您似乎正在尝试测试EFBlogRepository,因此您不应该嘲笑该类,您应该使用EFBlogRepository本身并仅仅模拟其依赖项。虽然我不知道你的FakeDbSet<Blog>究竟是什么,但你应该可以做这样的事情来获得正确的数据集:

var blogs = new List<Blog> { ... };
var blogDbSet = new FakeDbSet<Blog>(blogs.AsQueryable());

mockDbContext.SetupGet(c => c.Blogs).Returns(blogDbSet);

Blogs为空的原因是因为blogDbSet实际上并未配置为返回blogsQueryable

答案 1 :(得分:0)

您可以将方法声明为internal,并在您的案例单元测试项目的输出程序集中将内部暴露给特定的Assembly。

需要在正在测试的项目的AssemblyInfo.cs中提及以下属性。 [assembly:InternalsVisibleToAttribute("UnitTestAssemblyName")]

如果命名强烈

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("BoardEx_BusinessObjects.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fb3a2")]

有一个类似的线程回答相同的click here