-2

First the code,

Generic Interface:

public interface IEntityService<TEntity> where TEntity : class
{
    IQueryable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
      Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
      string includeProperties = "");

    Task<TEntity> GetByIDAsync(object id);

    Task<TEntity> GetFirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
}

Generic Class with interface implementation:

public class EntityService<TEntity> : IEntityService<TEntity> where TEntity : class
{
    protected IContext IContext;
    protected DbSet<TEntity> IDbSet;

    public EntityService(IContext context)
    {
        IContext = context;
        IDbSet = IContext.Set<TEntity>();
    }

    public virtual IQueryable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<TEntity> query = IDbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        query = includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Aggregate(query, (current, includeProperty) => current.Include(includeProperty));

        if (orderBy != null)
        {
            return orderBy(query);
        }
        return query;
    }

    public virtual async Task<TEntity> GetByIDAsync(object id)
    {
        return await IDbSet.FindAsync(id);
    }

    public virtual async Task<TEntity> GetFirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
    {
        return await IDbSet.FirstOrDefaultAsync(predicate);
    }
}

Specific interface:

public interface ILoginService
{
    Task<UserProfileViewModel> GetLoginDetailAsync(string userName);
}

Specific class: Implementing generic class and specific interface

public class LoginService : EntityService<UserAccount>, ILoginService
{

    private readonly IContext _iContext;

    public LoginService(IContext context): base(context)
    {
        _iContext = context;
    }

    async Task<UserProfileViewModel> ILoginService.GetLoginDetailAsync(string userName)
    {
        var userAcount = await GetFirstOrDefaultAsync(c => c.Username.ToLower() == userName.Trim().ToLower() && c.Active == true);
        if (userAcount != null)
        {
            return Mapper.Map<UserAccount, UserProfileViewModel>(userAcount);
        }
        return null;
    }
}

Now, I am supposed to test LoginService the one and only method it has

Here's the test code

    [Test]
    public async Task GetLoginDetailAsync_InvalidUsername_ReturnsNull()
    {
        var userName = "should not exist!";
        var userAccount = new List<UserAccount>()
        {
            new UserAccount
            {
                ID = 1,
                Name = "Test User"
            }
        }.AsQueryable();
        var mockSet = new Mock<DbSet<UserAccount>>();
        var userProfileViewModel = new UserProfileViewModel
        {
            ID = 1,
            Name = Guid.NewGuid().ToString().Substring(0, 8)
        };
        _context.Setup(c => c.Set<UserAccount>()).Returns(mockSet.Object);
        loginService = new LoginService(_context.Object);
        mockSet.As<IDbAsyncEnumerable<UserAccount>>().
            Setup(m => m.GetAsyncEnumerator()).
            Returns(new TestDbAsyncEnumerator<UserAccount>(userAccount.GetEnumerator()));
        mockSet.As<IQueryable<UserAccount>>()
            .Setup(m => m.Provider)
            .Returns(new TestDbAsyncQueryProvider<UserAccount>(userAccount.Provider));
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Expression).Returns(userAccount.Expression);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.ElementType).Returns(userAccount.ElementType);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.GetEnumerator()).Returns(userAccount.GetEnumerator());

        var result = await ((ILoginService)loginService).GetLoginDetailAsync(userName);
        Assert.IsNull(result);
    }

Now, these TestDbAsyncEnumerator and TestDbAsyncQueryProvider are taken from msdn to test Async queries in EF.

The problem

The test throws an exception, that Message: System.NotImplementedException : The member 'IQueryable.Provider' has not been implemented on type 'DbSet1Proxy' which inherits from 'DbSet1'. Test doubles for 'DbSet1' must provide implementations of methods and properties that are used. Basically, I have not setup the FirstOrDefaultAsync for mockSet that is getting called in GetLoginDetailAsync (it calls to EntityService, that ends up calling the FirstOrDefaultAsync of IDbSet).

I don't know how can I mock that, because the LoginService doesn't directly inherits it. It inherit's the EntityService which in turn has that generic method FirstOrDefaultAsync. I am stuck at how to set up that.

One another thing that I thought was try this

var loginMock = new Mock<LoginService>(_context.Object);
loginMock.As<ILoginService>().Setup(c => c.GetLoginDetailAsync(It.IsAny<string>())).Returns(Task.FromResult<UserProfileViewModel>(null));
loginMock.As<IEntityService<UserAccount>>().Setup(c => c.GetFirstOrDefaultAsync(It.IsAny<Expression<Func<UserAccount, bool>>>())).Returns(Task.FromResult(userAccount.First()));

But I don't think this is the correct way to go, as I would only be testing the mock object. Can anyone suggest me how do I get to setup and test/mock this GetFirstOrDefaultAsync, or am I totally going in a wrong direction?

UPDATE AFTER ANSWER:

After the answer from @ODawgG, I am updating this. The test worked fine as specified in answer, but now the other test is failing. I wanted to test, if a particular user exits in the system.

Here's the test code: [Test] public async Task Test3() {

        var userAccount = new List<UserAccount>()
        {
            new UserAccount
            {
                ID = 1,
                Username = "User"
            }
        }.AsQueryable();
        var mockSet = new Mock<DbSet<UserAccount>>();
        mockSet.As<IDbAsyncEnumerable<UserAccount>>().
            Setup(m => m.GetAsyncEnumerator()).
            Returns(new TestDbAsyncEnumerator<UserAccount>(userAccount.GetEnumerator()));
        mockSet.As<IQueryable<UserAccount>>()
            .Setup(m => m.Provider)
            .Returns(new TestDbAsyncQueryProvider<UserAccount>(userAccount.Provider));
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Expression).Returns(userAccount.Expression);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.ElementType).Returns(userAccount.ElementType);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.GetEnumerator()).Returns(userAccount.GetEnumerator());

        AutoMapConfiguration.Configure();
        var entityService = new Mock<IEntityService<UserAccount>>();

        entityService
            .Setup(service => service.GetFirstOrDefaultAsync(It.IsAny<Expression<Func<UserAccount, bool>>>()))
            .ReturnsAsync(
                (Expression<Func<UserAccount, bool>> predicate) => userAccount.FirstOrDefault(predicate)
            );

        var loginService = new LoginService(entityService.Object);

        // Act
        var result = await ((ILoginService)loginService).GetLoginDetailAsync("User");

        // Assert
        Assert.IsNotNull(result);
    }

This test should pass, as it should query on the userAccount but it fails, when I was debugging, and it went inside the LoginService, and I checked _entityService.Get().ToList() it says 0 count, while it should really say count 1, the userAccount that I have setup. Afaik, the IDbSet is still not setup, and that's why the count is 0, and it's not returning true. How do I setup that? If it is correct, then why is this test failing? Also, I know that moq isn't really good for testing expression, but I got this predicate part of code from here.

Razort4x
  • 3,296
  • 10
  • 50
  • 88
  • 2
    Have you tried mocking with `IDbSet` instead of `DbSet`. `IDbSet` was introduced in EF 5. You can then provide a value for the `Provider` property. – Hintham Oct 03 '17 at 15:54
  • 2
    Seems strange that `LoginService` inherit from actual implementation of `EntityService`? You made `Login Service to be tightly coupled to the EF implementation. From testing point of view `LoginService` should take abstraction of `IEntityService` as parameter in the constructor for example. Then you can write tests that expected predicate passed to the `_entityService.GetFirstOrDefaultAsync` method. – Fabio Oct 03 '17 at 17:23
  • @Downvoter: I have explained all the code, the issue I had, and the steps I took to fix my problem, and where they failed. I don't know how can I further improve my question. If you think there is still something wrong, the reason why you downvoted, please at least share your feedback/insight as to why you didn't like the question, what was missing, or how you think I can improve the question further. I don't think downvoting without explaining a reason is going to be productive for either me, or anyone answering or reading this question, thanks! – Razort4x Oct 04 '17 at 14:34

1 Answers1

2

I agree with @Fabio. There no need to inherit from EntityService<T> but rather inject into your LogService class.

Refactored your class would look like the following:

public class LoginService : ILoginService
{
    private readonly IEntityService<UserAccount> _entityService;

    public LoginService(IEntityService<UserAccount> entityService) 
    {
        _entityService = entityService;
    }

    async Task<UserProfileViewModel> ILoginService.GetLoginDetailAsync(string userName)
    {
        var userAcount = await _entityService.GetFirstOrDefaultAsync(c => c.Username.ToLower() == userName.Trim().ToLower() && c.Active);
        if (userAcount != null)
        {
            return Mapper.Map<UserAccount, UserProfileViewModel>(userAcount);
        }
        return null;
    }
}

And your test would look like this:

[Test]
public async Task GetLoginDetailAsync_InvalidUsername_ReturnsNull()
{
    // Arrange
    MapperInitialize.Configure();
    var entityService = new Mock<IEntityService<UserAccount>>();

    entityService
        .Setup(service => service.GetFirstOrDefaultAsync(It.IsAny<Expression<Func<UserAccount, bool>>>()))
        .ReturnsAsync(new UserAccount
        {
            ID = 1,
            Name = "Test User"
        });

    var loginService = new LoginService(entityService.Object);

    // Act
    var result = await ((ILoginService)loginService).GetLoginDetailAsync(It.IsAny<string>());

    // Assert
    Assert.IsNotNull(result);
}

Here's the updated test to include testing the expression:

    [Test]
    public async Task GetLoginDetailAsync_InvalidUsername_ReturnsNull()
    {
        // Arrange
        MapperInitialize.Configure();
        var entityService = new Mock<IEntityService<UserAccount>>();

        var userAccount = new UserAccount
        {
            ID = 1,
            Username = "Test User",
            Active = true
        };

        var expressionResult = false;
        entityService
            .Setup(service => service.GetFirstOrDefaultAsync(It.IsAny<Expression<Func<UserAccount, bool>>>()))
            .Callback<Expression<Func<UserAccount, bool>>>(expression =>
            {
                expressionResult = expression.Compile().Invoke(userAccount);
            })
            .ReturnsAsync(userAccount);

        var loginService = new LoginService(entityService.Object);

        // Act
        var result = await ((ILoginService)loginService).GetLoginDetailAsync("Test User");

        // Assert
        Assert.IsTrue(expressionResult);
        Assert.IsNotNull(result);
    }
TheRock
  • 1,513
  • 1
  • 18
  • 19
  • Is constructor injection a good way to go? I mean I could also inherit from `IEntityService`, something like this, `public class LoginService : ILoginService, IEntityService` but in that case I would have to implement all these generic method in this class, which might be redundant. – Razort4x Oct 04 '17 at 00:39
  • Yes it's definitely good practice to inject through the constructor. In SOLID principles the D stands for 'Dependency Inversion Principle'. So LoginService no longer has a direct dependency on IEntityService but rather depends on an abstraction. This is key to your design when managing dependencies, it greatly helps with proper unit testing and making use mocking frameworks and IOC containers. – TheRock Oct 04 '17 at 02:14
  • I've added some more details, please can you check that part? – Razort4x Oct 04 '17 at 07:34
  • So tho object that your mocking (setting up) and passing to LoginService is `IEntityService`. Simply set the function GetFirstOrDefaultAsync to return the desired object much like I have in my answer `entityService.Setup(service => service.GetFirstOrDefaultAsync(It.IsAny>>())) .ReturnsAsync(new UserAccount { ID = 1, Name = "Test User" });` – TheRock Oct 04 '17 at 13:21
  • Yeah that'll work, but I want to check the expression, basically, like I have written. Instead of hard-coding a return value, like you have, I want to test the expression against against the mock collection (the `userAccount` in the example). – Razort4x Oct 04 '17 at 14:25
  • If your testing the expression you can setup a Callback on the mock and then test the expression `var expressionResult = false; entityService.Setup(service => service.GetFirstOrDefaultAsync(It.IsAny>>())) .Callback>>(expression => { expressionResult = expression.Compile().Invoke(userAccountList); }).ReturnsAsync(userAccountList);` You can then Assert the bool `Assert.IsTrue(expressionResult);` – TheRock Oct 04 '17 at 14:54
  • `Expression>` is an expression tree of `Func` so the `expression.Compile().Invoke(userAccountList);` just means that your are compiling the expression and then invoking the Func on the object that is passed to it. So expressionResult will be true or false depending on the userAccount that passed in as a parameter – TheRock Oct 04 '17 at 15:01
  • I have updated the answer to include testing the expression. – TheRock Oct 04 '17 at 15:04