I\'ve read that when using moq you cannot mock a non-virtual function. I\'ve also read that this should be possible now.. Is that true? If so, then I\'d like to mock the f
It is possible to mock ExecuteSqlCommand
. It is however not straightforward.
Being an extension method you have to mock the internals. It ends up creating a RawSqlCommand
and invoking ExecuteNonQuery
on an IRelationalCommand
. It gets further complicated as the extension method creates new objects to do the actual work, as well as there being is no interface for RawSqlCommand
or DatabaseFacade
, you've got to mock the concrete classes.
I ended up writing EntityFrameworkCore.Testing as there isn't anything else around that can do all the mocks (FromSql, ExecuteSqlCommand, DbQuery, the relational stuff that the in-memory provider can't do). Save yourself some time as the mocks are involved, I certainly would have used an existing package if there was one when I was looking.
If you do want to roll your own the mock set up for ExecuteSqlCommand
/ExecuteSqlCommandAsync
looks like the following:
var relationalCommandMock = new Mock();
relationalCommandMock
.Setup(m => m.ExecuteNonQuery(It.IsAny(), It.IsAny>()))
.Returns((IRelationalConnection providedConnection, IReadOnlyDictionary providedParameterValues) => executeSqlCommandResult);
relationalCommandMock
.Setup(m => m.ExecuteNonQueryAsync(It.IsAny(), It.IsAny>(), It.IsAny()))
.Returns((IRelationalConnection providedConnection, IReadOnlyDictionary providedParameterValues, CancellationToken providedCancellationToken) => Task.FromResult(executeSqlCommandResult));
var relationalCommand = relationalCommandMock.Object;
var rawSqlCommandMock = new Mock(MockBehavior.Strict, relationalCommand, new Dictionary());
rawSqlCommandMock.Setup(m => m.RelationalCommand).Returns(relationalCommand);
rawSqlCommandMock.Setup(m => m.ParameterValues).Returns(new Dictionary());
var rawSqlCommand = rawSqlCommandMock.Object;
var rawSqlCommandBuilderMock = new Mock();
rawSqlCommandBuilderMock
.Setup(m => m.Build(It.IsAny(), It.IsAny>()))
.Returns((string providedSql, IEnumerable
executeSqlCommandResult
is the expected integer to return. You could use a callback to apply any operation said sql command performs on your data source.
Note that I am passing the mocked db context to the database facade mock constructor then performing another Moq set up on the mocked database, I get away with this as I have already performed a set up on the DbContext Database property as part of mocking the db context. I have observed test success with an inline new instance so I don't think it matters, just in my case I didn't want to spin up another instance.