0

I have a repository that has a few private methods in to help in some general stuff that needs to be done within that repository (don't feel you need to read all the code):

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();
    }

The problem comes when I try and unit test this...

I have had to make the PrepareAllBlogsQuery public and virtual to get this to work (You just need to read the commented bit):

// 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);

Is there any way around this?

It's not like I even want to test the PrepareAllBlogsQuery method, I just need the mock framework to know what that method returns regardless of its content.

Jimmyt1988
  • 20,466
  • 41
  • 133
  • 233

2 Answers2

1

Yes, you should create an interface for your repository which is the thing you mock in your unit test:

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

public class EFBlogRepository : IEFBlogRepository
{
   ...
}

Then in your unit test, you can mock the IEFBlogRepository and you don't need to go anywhere near EF.

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

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

Update

Since it seems you are trying to test EFBlogRepository, you shouldn't be mocking that class, you should use EFBlogRepository itself and just mock its dependencies. You should be able to do something like this to get the correct DB set although I don't know what your FakeDbSet<Blog> actually is:

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

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

The reason Blogs is null for you is because blogDbSet isn't actually configured to return the blogsQueryable

Trevor Pilley
  • 16,156
  • 5
  • 44
  • 60
  • The problem is with the `PrepareAllBlogsQuery`, I have to SetupGet using Moq on it... And yet I can't because it's a private method. Otherwise if I test `GetAllBlogs`, that first call to `PrepareAllBlogsQuery` returns `null`. I don't really want the private method inside my interface, it's not a required method to be filled out... simply a helper method inside the repository for my interface methods. – Jimmyt1988 Jun 18 '15 at 09:02
  • @Jimmyt1988 I don't actually know what you're trying to test here, I thought you were trying to test something which depends on `EFBlogRepository`? If that's not the case, are you trying to test `EFBlogRepository` itself? – Trevor Pilley Jun 18 '15 at 09:06
  • 1
    Mock the context class that the PrepareAllBlogsQuery is calling. That way you know precisely that the private method will return – uk2k05 Jun 18 '15 at 09:07
  • @TrevorPilley - Yes, trying to test the repository :) – Jimmyt1988 Jun 18 '15 at 09:09
  • @uk2k05, I think I'll give the context mocking a try (although god forbid I can find out all the interface requirements for it ) – Jimmyt1988 Jun 18 '15 at 09:09
  • 1
    @Jimmyt1988 You shouldn't create a mock repository if that's the thing you want to test, you should just mock the dbcontext and use the actual `EFBlogRepository` – Trevor Pilley Jun 18 '15 at 09:10
  • Thanks Trevor, I'll give this a try – Jimmyt1988 Jun 18 '15 at 09:11
0

You can have the method declared as internal and expose internals to specific Assembly in your case unit test project's output assembly.

Below attributes needs to be mentioned in the AssemblyInfo.cs of the project being tested. [assembly:InternalsVisibleToAttribute("UnitTestAssemblyName")]

In case of strongly named

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

there's a similar thread answering the same click here

Community
  • 1
  • 1
Arick
  • 21
  • 5
  • You shouldn't use this approach to test private members of public classes, you should only use it to test internal classes. Unit tests should be concerned with the class API (e.g. the publicly visible methods and properties) if you find you need to make a private method internal, you're going about things the wrong way. – Trevor Pilley Jun 18 '15 at 09:14