When performing unit tests, libraries such as Moq are often used to mock the code. However, in some cases, we want to not only mock methods but also determine if the passed parameters are correct within the mock methods.
This is because conventional mocking simply returns a value.
var mock = new Mock<Test>();
mock.Setup<xxxx>(x => Get)....
var obj = mock.Object;
var result = obj.Get();
At this point, parameters and calculation processes are ignored, and a value is returned.
However, if we need to validate a parameter based on its value, we require a different approach.
The author's requirement is for a database synchronization task that generates SQL based on changing tables or data, and then executes the SQL against the target database. Since this is a unit test, I need to mock the database, specifically mocking the SQLCommand and SQLConnect processes.
I created an interface to further decouple the database connection:
public class MysqlDatabaseMock : MysqlDatabase
...
protected override async Task ExecteAsync(MySqlCommand dbCommand, string sql)
{
_output.WriteLine(sql);
await Task.CompletedTask;
}
...
Here, it is a
protected
method.
As can be seen, it returns a Task, meaning this method has no return value; therefore, we cannot validate whether the SQL generated matches our expectations by acquiring a return value. What we need to do is execute our code within the mock method and ensure that Assert.Equal(left, right);
works. This means that our Assert.Equal
is not placed in the test method, but instead directly in the mock.
void xxxTest()
{
mock.Get()
... ...
Get => Assert.Equal() ....
}
The actual implementation is as follows:
private static void MockExcete(string sqlStr, Mock<MysqlDatabaseMock> mock)
{
Func<MySqlConnection, string, Task> func = (connect, sql) =>
{
var left = Format(sqlStr);
var right = Format(sql);
Assert.Equal(left, right);
return Task.CompletedTask;
};
mock.Protected().Setup<Task>("ExecteAsync", ItExpr.IsAny<MySqlConnection>(), ItExpr.IsAny<string>())
.Returns((Delegate)func);
mock.Protected().Setup<Task>("ExecteAsync", ItExpr.IsAny<MySqlCommand>(), ItExpr.IsAny<string>())
.Returns((Delegate)func);
static string Format(string sql)
{
var newSql = sql
.Replace("\r\n", "")
.Replace("\r", "")
.Replace("\n", "")
.Replace(" ", "");
if (newSql.EndsWith(';'))
{
return newSql[..^1];
}
return newSql;
}
}
The code is straightforward; we use a delegate to allow the mock to execute this method.
文章评论