我必须制作一个方法 public 并虚拟使用 Moq Setup

I have to make a method public and virtual to using Moq Setup

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

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 方法,我只需要模拟框架知道该方法是什么 returns 而不管其内容如何。

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

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,你不应该嘲笑 class,你应该使用 EFBlogRepository 本身并仅仅嘲笑它的依赖关系。你应该能够做这样的事情来获得正确的数据库集,虽然我不知道你的 FakeDbSet<Blog> 实际上是什么:

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

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

Blogs 对您来说为空的原因是 blogDbSet 实际上并未配置为 return blogsQueryable

您可以将方法声明为内部方法,并将内部结构公开给案例单元测试项目输出程序集中的特定程序集。

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

在强命名的情况下

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

有一个类似的帖子回答相同 click here