如何使用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
相关产品推荐
相关产品推荐

