C# 如何模拟对内存数据执行oracle查询
我试图编写单元测试来执行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
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();
}
}