0

when unit testing PatIndex of SqlFunctions with a mocked IQueryable object I'm getting the following error:

"This function can only be invoked from LINQ to Entities."

Here is how I initialize my mock repositories:

protected void InitUnitOfWork<TEntity>(IEnumerable<TEntity> data)
    where TEntity : class
{
    var dataSet = Substitute.For<IDbSet<TEntity>, IQueryable<TEntity>>().Initialize(data.AsQueryable());
    var query = Substitute.For<IRepositoryQuery<TEntity>>();
    var repository = Substitute.For<IRepository<TEntity>>();

    this.UnitOfWork.Repository<TEntity>().Returns(repository);
    repository.Query().Returns(query);
    query.Include(null).ReturnsForAnyArgs(query);
    query.Filter(null).ReturnsForAnyArgs(query);
    query.Get().Returns(dataSet);
}

This is the method I'm trying to test:

public IEnumerable<ContactDto> GetContactsBySearchText(string searchText)
{
    var companyId = CurrentUser.User.CurrentCompany.Id;

    var contacts = this.GetIQueryableContacts().Where(x => x.CompanyMasterData.Id == companyId);

    if (!string.IsNullOrWhiteSpace(searchText))
    {
        var pattern = SearchQueryParserHelper.ParseString(searchText.Trim());
        contacts = contacts.Where(x => SqlFunctions.PatIndex(pattern, x.Name) > 0
            || SqlFunctions.PatIndex(pattern, x.Code) > 0
            || SqlFunctions.PatIndex(pattern, x.Phone) > 0);
    }

    return Mapper.Map<IQueryable<Contact>, IEnumerable<ContactDto>>(contacts);
}

I know I can fix this issue by using the database, but how can I mock it correctly?

I'm using NSubstitute as Mocking Framework.

Best regards

Daniel

Alexandr Nikitin
  • 7,258
  • 2
  • 34
  • 42
DAG
  • 2,460
  • 5
  • 33
  • 61

1 Answers1

1

Make use of Microsoft.QualityTools.Testing.Fakes.dll (Here is the link on how to use it: MSDN)

Later on generate Fakes assembly for System.Data.Entity.SqlServer.dll

Wrap the body of your Unit Test method following using statement:

using (Microsoft.QualityTools.Testing.Fakes.ShimsContext.Create())
{
     // Setup Shim function for PatIndex:
     System.Data.Entity.SqlServer.FakesShimSqlFunctions.PatIndexStringString = (pattern, target) => 
     {
        // Implement PatIndex function here
        throw new NotImplementedException();
     }

     // Your Unit test code goes here
}

Once you have provided a Shim (you can call it as a replacement) for PatIndex, you will be able to unit test your target method successfully.

mukala
  • 41
  • 8
  • 1
    We did this in our company, and it grew, and it now costs us an additional $65,000 per year because all devs need Visual Studio Enterprise just to ensure all unit tests pass. My job over summer is to basically remove all usage of the terrible DbFunctions libraries so we can downgrade people to VS Professional. – Ryan Dec 13 '18 at 02:57