内容目录
做单元测试的时候往往会使用 Moq 等库,对代码进行 Mock。
但是有些过程,我们希望除了 Mock 方法之外,能够在 Mock 方法中,判断传递的参数是否正确。
因为常规的 Mock ,是返回一个值。
var mock = new Mock<Test>();
mock.Setup<xxxx>(x => Get)....
var obj = mock.Object;
var result = obj.Get();
在这个时候,是忽略参数和计算过程,返回一个值。
但是我们如果需要一个参数,根据参数判断,是否验证通过。
笔者的需求是,一个数据库同步任务,根据变化的表或数据,生成 SQL ,然后向目标数据库执行 SQL。
因为是单元测试,所以我需要 Mock 数据库,即 Mock SQLCommand 好的 SQLConnect 两个过程。
我做了个接口,进一步解耦数据库连接:
public class MysqlDatabaseMock : MysqlDatabase
...
protected override async Task ExecteAsync(MySqlCommand dbCommand, string sql)
{
_output.WriteLine(sql);
await Task.CompletedTask;
}
...
这里是个
protected
方法。
可以看到,返回的是 Task,也就是这个方法没有返回值,因此,我们是不能通过获取返回值,判断这个 SQL 是否生成得跟我们的预估一样。我们要做的,就是在 Mock 方法中,执行我们的代码,并且,需要 让 Assert.Equal(left, right);
生效,也就是,我们这个 Assert.Equal
不是放在 Test 方法中,而是放在 Mock 中。
void xxxTest()
{
mock.Get()
... ...
Get => Assert.Equal() ....
}
实际写法:
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;
}
}
代码很简单,我们使用一个委托,让 Mock 执行这个方法。
文章评论