3

The problem statement looks lengthy but I have tried my best to make it as simple as possible.

I have a small function inside SchedulerLPOP10ReportDataView class which uses DbContext Object as:

public async Task<IEnumerable<T>> GetReportViewData<T>(OneFpsReportsDbContext dbContext)     
{
    var defaultTimeOut = dbContext.Database.GetCommandTimeout();
                            
    dbContext.Database.SetCommandTimeout(new TimeSpan(0,20,0));
    var results = await dbContext.VW_Report.FromSqlRaw($"SELECT * from [Analytics].[VW_LPOP10_Report_Scheduling]).ToListAsync();
    dbContext.Database.SetCommandTimeout(defaultTimeOut);
    return (IEnumerable<T>)(results);      
            
}

The OneFpsReportsDbContext class syntax looks like:

    public partial class OneFpsReportsDbContext : DbContext
    {
        public OneFpsReportsDbContext()
        {
        }
    
        public OneFpsReportsDbContext(DbContextOptions<OneFpsReportsDbContext> options)
            : base(options)
        {
        }
       //code
    }

I have written test case as:

[Fact]
public void GetReportViewData_OK()
{
    // Arrange
    
    var unitUnderTest = new SchedulerLPOP10ReportDataView(It.IsAny<int>());

    var mockSet = new Mock<DbSet<DbContext>>();
    var MockOneFpsReportsDbContext1 = new Mock<OneFpsReportsDbContext>();
    TimeSpan ts3 = TimeSpan.FromMinutes(20);
    MockOneFpsReportsDbContext1.Setup(x => x.Database.SetCommandTimeout(ts3)); //[1] error

    //Act
    var res = unitUnderTest.GetReportViewData<ISchedularReportDataView>(MockOneFpsReportsDbContext1.Object, WhereClause, sqlParameters).Result;

    //Assert
}

At [1] error I get this:

Unsupported expression: ... => ....SetCommandTimeout(ts3) Extension methods (here: RelationalDatabaseFacadeExtensions.SetCommandTimeout) may not be used in setup / verification expressions.

I also tried the 'UseInMemoryDatabase' approach but I came to know we can't use 'GetCommandTimeout' with it. What Am I missing or doing incorrect in the //Arrange section of my test case? And How exactly can I make this mock / work? Thanks.

2
  • 2
    What do you expect to meaningfully unit test in this method if the dbContext is a mock? You probably want an integration test using the in memory database as you mention. Maybe you want to hide the part of the functionality that does not work in the in memory version behind an interface or a virtual method, so that you can replace it with a noop implementation when you run the test? Commented Jan 9, 2023 at 13:24
  • 1
    It is not possible to mock static methods with Moq. Extension methods are just syntactic sugar on top of static methods Commented Jan 9, 2023 at 13:24

1 Answer 1

1

This is, unfortunately, one of the hard-to-overcome limitations of XUnit/in-memory DB combo. You basically have two choices:

  • Spin up an actual test database locally for your unit testing purposes. In this case you'll be closer to production, but your test would kinda turn from unit into integration since you'll lack proper isolation

  • Create a wrapper class around the DbContext plus extension methods you need and mock that class instead.

The latter will look like this:

public class DbContextWrapper: OneFpsReportsDbContext
{
    public void SetCommandTimeout(TimeSpan ts)
    {
        this.Database.SetCommandTimeout(ts);
    }
}

// Then inside the usage block
public async Task<IEnumerable<T>> GetReportViewData<T>(DbContextWrapper dbContext)     
{
    var defaultTimeOut = dbContext.Database.GetCommandTimeout();
                            
    dbContext.SetCommandTimeout(new TimeSpan(0,20,0));
    var results = await dbContext.VW_Report.FromSqlRaw($"SELECT * from [Analytics].[VW_LPOP10_Report_Scheduling]").ToListAsync();
    dbContext.Database.SetCommandTimeout(defaultTimeOut);
    return (IEnumerable<T>)(results);                 
}

// And in the test:

[Fact]
public void GetReportViewData_OK()
{
    // Arrange
    
    var unitUnderTest = new SchedulerLPOP10ReportDataView(It.IsAny<int>());

    var mockSet = new Mock<DbSet<DbContext>>();
    var MockOneFpsReportsDbContext1 = new Mock<DbContextWrapper>();
    TimeSpan ts3 = TimeSpan.FromMinutes(20);
    MockOneFpsReportsDbContext1.Setup(x => x.SetCommandTimeout(ts3)); //[1] error

    //Act
    var res = unitUnderTest.GetReportViewData<ISchedularReportDataView>(MockOneFpsReportsDbContext1.Object, WhereClause, sqlParameters).Result;

    //Assert
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.