C# 如何模拟对内存数据执行oracle查询

C# 如何模拟对内存数据执行oracle查询,c#,unit-testing,oracle11g,oracle-sqldeveloper,moq,C#,Unit Testing,Oracle11g,Oracle Sqldeveloper,Moq,我试图编写单元测试来执行oracle查询,以过滤内存中的对象列表 如何模拟,以便将筛选条件应用于内存中的对象列表,而不是实际的数据库 我可以通过实体框架来实现这一点,在实体框架中,我可以模拟上下文并返回内存中的数据,但不知道如何使用OracleCommand.ExecuteReader实现同样的效果 using (var connection = new OracleConnection(connectionString)) { connection.Open(); var c

我试图编写单元测试来执行oracle查询,以过滤内存中的对象列表

如何模拟,以便将筛选条件应用于内存中的对象列表,而不是实际的数据库

我可以通过实体框架来实现这一点,在实体框架中,我可以模拟上下文并返回内存中的数据,但不知道如何使用
OracleCommand.ExecuteReader
实现同样的效果

using (var connection = new OracleConnection(connectionString))
{
    connection.Open();

    var cmd = new OracleCommand
        {
            //TODO add Reg_Date in Where clause
            Connection = connection,
            CommandText =
                "SELECT mi.* from fromTable mi where 1=1 " 
                + (string.IsNullOrEmpty(nuf) ? "" : " and mi.NUF != '"+ nuf +"'")
                + " and mi.Category<>'TES' and mi.Category<>'CVD'"
            CommandType = CommandType.Text
        };

    Debug.WriteLine(cmd.CommandText);

    var dr = cmd.ExecuteReader();
}
使用(var连接=新的OracleConnection(connectionString))
{
connection.Open();
var cmd=新的OracleCommand
{
//TODO在Where子句中添加注册表日期
连接=连接,
命令文本=
“从表mi中选择mi.*,其中1=1”
+(string.IsNullOrEmpty(nuf)?“”:“”和mi.nuf!=“+nuf+”)
+“和mi.‘TES’类和mi.‘CVD’类”
CommandType=CommandType.Text
};
Debug.WriteLine(cmd.CommandText);
var dr=cmd.ExecuteReader();
}

目前,测试中的方法与实现问题的耦合过于紧密,无法使其易于单独进行单元测试。尝试将这些实现关注点抽象出来,以便可以轻松地对它们进行模拟,以进行独立测试

public interface IDbConnectionFactory {
    IDbConnection CreateConnection();
}
上述连接工厂抽象可用于访问Oracle数据存储的其他必要的
System.Data
抽象

public class MyDataAccessClass {
    private IDbConnectionFactory connectionFactory;

    public MyDataAccessClass(IDbConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    public object GetData(string nuf) {
        using (var connection = connectionFactory.CreateConnection()) {
            connection.Open();
            var query = "SELECT mi.* from fromTable mi where 1=1 " 
                        + (string.IsNullOrEmpty(nuf) ? "" : " and mi.NUF != @nuf")
                        + " and mi.Category<>'TES' and mi.Category<>'CVD'"
            using(var command = connection.CreateCommand()){
                command.CommandText = query;
                command.CommandType = CommandType.Text;
                if(!string.IsNullOrEmpty(nuf)) {
                    var parameter = command.CreateParameter();
                    parameter.ParameterName = "@nuf";
                    parameter.Value = nuf;

                    command.Parameters.Add(parameter);
                }

                Debug.WriteLine(command.CommandText);

                var dr = command.ExecuteReader();

                //...other code removed for brevity
            }
        }
    }
}
可以通过依赖项注入将其传递到依赖类中

为了进行测试,您可以使用您选择的模拟框架模拟接口,或者创建您自己的赝品来注入和测试您的方法

[TestClass]
public class DataAccessLayerUnitTest {
    [TestMethod]
    public void TestFilter() {
        //Arrange
        var readerMock = new Mock<IDataReader>();

        var commandMock = new Mock<IDbCommand>();
        commandMock.Setup(m => m.ExecuteReader())
            .Returns(readerMock.Object)
            .Verifiable();

        var parameterMock = new Mock<IDbDataParameter>();            

        commandMock.Setup(m => m.CreateParameter())
            .Returns(parameterMock.Object);

        commandMock.Setup(m => m.Parameters.Add(It.IsAny<IDbDataParameter>()))
            .Verifiable();

        var connectionMock = new Mock<IDbConnection>();
        connectionMock
            .Setup(m => m.CreateCommand())
            .Returns(commandMock.Object);

        var connectionFactoryMock = new Mock<IDbConnectionFactory>();
        connectionFactoryMock
            .Setup(m => m.CreateConnection())
            .Returns(connectionMock.Object);

        var sut = new MyDataAccessClass(connectionFactoryMock.Object);
        var input = "some value";

        //Act
        var data = sut.GetData(input);

        //Assert
        commandMock.Verify();
    }
}
[TestClass]
公共类DataAccessLayerUnitTest{
[测试方法]
公共void TestFilter(){
//安排
var readerMock=new Mock();
var commandMock=new Mock();
commandMock.Setup(m=>m.ExecuteReader())
.Returns(readerMock.Object)
.可验证();
var parameterMock=new Mock();
commandMock.Setup(m=>m.CreateParameter())
.Returns(parameterMock.Object);
commandMock.Setup(m=>m.Parameters.Add(It.IsAny()))
.可验证();
var connectionMock=new Mock();
连接锁
.Setup(m=>m.CreateCommand())
.Returns(commandMock.Object);
var connectionFactoryMock=new Mock();
connectionFactoryMock
.Setup(m=>m.CreateConnection())
.Returns(connectionMock.Object);
var sut=新的MyDataAccessClass(connectionFactoryMock.Object);
var input=“某些值”;
//表演
var data=sut.GetData(输入);
//断言
commandMock.Verify();
}
}

最后,建议您在命令文本中使用命令参数,因为使用外部值手动构造查询字符串会打开SQL注入攻击的代码。

因此,当您执行cmd时,您得到了什么或没有得到什么。还可以尝试构造查询,或者调用存储过程,但不要使用连接字符串来构造查询。。您正在设置sql注入。当然。稍后我将从内联sql转移到存储过程。这将执行对实际数据库的查询。我想查询内存中的数据并应用筛选器,以便进行单元测试。请尝试将数据作为DataTable返回,并使用DataTables
Filer
函数。如果你在C#StackOverflow DataTable上进行
Google搜索,那么在StackOverflow和internet上都有很多这样做的工作示例。Filter
会产生很多结果。这会影响性能吗?有没有其他方法来实现这一点?到目前为止,我知道内存中的数据库将只支持EF核心。请参阅关于EF Core和Oracle的讨论。最好的方法是重写代码并使用IDbConnection等接口。这些接口可以模拟。这听起来不错,但是如果
CreateConnection()
必须返回可模拟的ÌDbConnection`(这是本练习的重点),它就不能返回OracleConnection。这种情况使得整个想法无法实现,这是一种耻辱。@oerkelens你指的是什么
OracleConnection
是源于
IDbConnection
的一个实现问题。我认为你误解了这里的概念。我开始定义DbConnection和IDbConnection,没有意识到它们已经存在,OrackConnection实现了System.Data.DbConnection。现在觉得自己很愚蠢。。。并愉快地继续你的解决方案!只要您不使用任何Oracle功能或不依赖任何Oracle怪癖,DbConnection仅作为对Oracle的抽象有用。比如说,当您不得不使用BindByName来避免对参数排序时,抽象就会被抛出窗口。
[TestClass]
public class DataAccessLayerUnitTest {
    [TestMethod]
    public void TestFilter() {
        //Arrange
        var readerMock = new Mock<IDataReader>();

        var commandMock = new Mock<IDbCommand>();
        commandMock.Setup(m => m.ExecuteReader())
            .Returns(readerMock.Object)
            .Verifiable();

        var parameterMock = new Mock<IDbDataParameter>();            

        commandMock.Setup(m => m.CreateParameter())
            .Returns(parameterMock.Object);

        commandMock.Setup(m => m.Parameters.Add(It.IsAny<IDbDataParameter>()))
            .Verifiable();

        var connectionMock = new Mock<IDbConnection>();
        connectionMock
            .Setup(m => m.CreateCommand())
            .Returns(commandMock.Object);

        var connectionFactoryMock = new Mock<IDbConnectionFactory>();
        connectionFactoryMock
            .Setup(m => m.CreateConnection())
            .Returns(connectionMock.Object);

        var sut = new MyDataAccessClass(connectionFactoryMock.Object);
        var input = "some value";

        //Act
        var data = sut.GetData(input);

        //Assert
        commandMock.Verify();
    }
}