Reputation: 75
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.
Upvotes: 3
Views: 971
Reputation: 10487
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
}
Upvotes: 1