You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Moq模拟MySqlConnection相关方法并编写测试用例

解决Moq模拟MySqlConnection相关方法的问题

你遇到的核心问题是MySqlConnection和MySqlCommand都是密封类,而Moq默认只能模拟接口、抽象类或带有虚方法的类,直接模拟这些密封类必然会报错。下面是具体的解决思路和实现:

1. 重构代码,依赖抽象而非具体类型

不要直接依赖MySqlConnection和MySqlCommand,改用它们的抽象基类DbConnection和DbCommand(MySql的实现类都继承自这些基类),这样就能用Moq模拟这些基类的虚方法。

重构后的MySQLServerConnection类:

public class MySQLServerConnection 
{
    public async virtual Task<DbDataReader> ExecuteReaderAsync(DbCommand command, DbConnection connection)
    {
        try
        {
            return await command.ExecuteReaderAsync();
        }
        catch (MySqlException ex)
        {
            throw ex;
        }
    }

    public async Task<object> RetryExecuteScalarAsync(DbCommand command, DbConnection connection)
    {
        try
        {
            return await command.ExecuteScalarAsync();
        }
        catch (MySqlException ex)
        {
            throw ex;
        }
    }
}

2. 编写Moq测试用例

现在可以用Moq模拟DbConnection和DbCommand,测试ExecuteReaderAsync和RetryExecuteScalarAsync的逻辑:

测试ExecuteReaderAsync成功场景

[Test]
public async Task ExecuteReaderAsync_ShouldReturnReader_WhenCommandExecutesSuccessfully()
{
    // Arrange
    var mockCommand = new Mock<DbCommand>();
    var mockReader = new Mock<DbDataReader>();
    mockCommand.Setup(c => c.ExecuteReaderAsync(CancellationToken.None))
               .ReturnsAsync(mockReader.Object);

    var connection = new Mock<DbConnection>().Object;
    var dbServerConnection = new MySQLServerConnection();

    // Act
    var result = await dbServerConnection.ExecuteReaderAsync(mockCommand.Object, connection);

    // Assert
    Assert.NotNull(result);
    mockCommand.Verify(c => c.ExecuteReaderAsync(CancellationToken.None), Times.Once);
}

测试ExecuteReaderAsync抛出异常场景

[Test]
public async Task ExecuteReaderAsync_ShouldThrowMySqlException_WhenCommandFails()
{
    // Arrange
    var mockCommand = new Mock<DbCommand>();
    var testException = new MySqlException("Test exception");
    mockCommand.Setup(c => c.ExecuteReaderAsync(CancellationToken.None))
               .ThrowsAsync(testException);

    var connection = new Mock<DbConnection>().Object;
    var dbServerConnection = new MySQLServerConnection();

    // Act & Assert
    var exception = await Assert.ThrowsAsync<MySqlException>(() => 
        dbServerConnection.ExecuteReaderAsync(mockCommand.Object, connection));
    Assert.AreEqual("Test exception", exception.Message);
}

测试RetryExecuteScalarAsync成功场景

[Test]
public async Task RetryExecuteScalarAsync_ShouldReturnResult_WhenCommandExecutesSuccessfully()
{
    // Arrange
    var mockCommand = new Mock<DbCommand>();
    var testResult = "Test Result";
    mockCommand.Setup(c => c.ExecuteScalarAsync(CancellationToken.None))
               .ReturnsAsync(testResult);

    var connection = new Mock<DbConnection>().Object;
    var dbServerConnection = new MySQLServerConnection();

    // Act
    var result = await dbServerConnection.RetryExecuteScalarAsync(mockCommand.Object, connection);

    // Assert
    Assert.AreEqual(testResult, result);
    mockCommand.Verify(c => c.ExecuteScalarAsync(CancellationToken.None), Times.Once);
}

额外说明

如果你的业务逻辑需要更细粒度的控制,也可以自定义数据库操作接口(比如IDbExecutor),将ExecuteReaderAsync和RetryExecuteScalarAsync封装到接口中,这样模拟会更灵活,也更符合依赖倒置原则。

内容的提问来源于stack exchange,提问作者kayal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 04:22:51