Issue mocking(moq) ExecuteAsync for unit test(xunit)
a moq of ExecuteAsync in my unit testing is throwingInnerException = {"Async operations require use of a DbConnection or an IDbConnection where .CreateCommand() returns a DbCommand"}. Execute seems to work fine.
private void SetupExecuteAsync ()
{
var dataParameterCollection = new Mock<IDataParameterCollection>();
dbCommandMock.Setup(x => x.Parameters).Returns(dataParameterCollection.Object);
dbCommandMock.Setup(x => x.ExecuteNonQuery()).Returns(It.IsAny<int>());
dbConnectionMock.Setup(s => s.CreateCommand()).Returns(dbCommandMock.Object);
}
I am mocking DbCommand object and Execute and ExecuteAsync seem to have the same parameters so I'm not sure why I am getting this error.
return connection.Execute(sql, dbParams, commandType: commandType);
vs
return await connection.ExecuteAsync(sql, dbParams, commandType: commandType).ConfigureAwait(false);
Can you share the actual type hierarchy of what's being mocked?
I just ran into this problem on my project too.
The test performs differently for connection.Execute(...) and connection.ExecuteAsync(...) because the async version calls this method:
private static DbCommand TrySetupAsyncCommand(this CommandDefinition command, IDbConnection cnn, Action<IDbCommand, object> paramReader)
{
if (command.SetupCommand(cnn, paramReader) is DbCommand dbCommand)
{
return dbCommand;
}
else
{
throw new InvalidOperationException("Async operations require use of a DbConnection or an IDbConnection where .CreateCommand() returns a DbCommand");
}
}
Notice that the above method is insuring that the generated command derives from the base class System.Data.Common.DbCommand. That will likely not be the case for your mocked implementation of a System.Data.Common.IDbCommand.
In case it helps anyone, I ran into this problem and ended up sub-classing what Dapper is expecting, and then delegating calls to NSubstitute:
Class:
private class DbCommandMock : DbCommand
{
private readonly IDbCommand _dbCommandStub;
public DbCommandMock(IDbCommand dbCommandStub)
{
_dbCommandStub = dbCommandStub;
}
public override void Cancel() => _dbCommandStub.Cancel();
public override int ExecuteNonQuery() => _dbCommandStub.ExecuteNonQuery();
public override object ExecuteScalar() => _dbCommandStub.ExecuteScalar();
public override void Prepare() => _dbCommandStub.Prepare();
public override string CommandText { get; set; }
public override int CommandTimeout { get; set; }
public override CommandType CommandType { get; set; }
public override UpdateRowSource UpdatedRowSource { get; set; }
protected override DbConnection DbConnection { get; set; }
protected override DbParameterCollection DbParameterCollection { get; }
protected override DbTransaction DbTransaction { get; set; }
public override bool DesignTimeVisible { get; set; }
protected override DbParameter CreateDbParameter() => new MySqlParameter();
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => throw new NotImplementedException();
}
Test setup:
var dbCommand = new DbCommandMock(Substitute.For<IDbCommand>());
var dbConnection = Substitute.For<IDbConnection>();
dbConnection.CreateCommand().Returns(dbCommand);
Same problem. Solution from @jchoca doesn't work for me.