gpt4 book ai didi

c# - 单元测试 - 模拟的一些困难

转载 作者:行者123 更新时间:2023-11-30 12:22:13 25 4
gpt4 key购买 nike

我正在处理一些遗留代码,我需要为其编写一些单元测试。存在具有以下签名的数据访问方法。

Task ExecuteReaderAsync(string procedureName, Parameters procedureParameters, 
params Action<System.Data.IDataReader>[] actions);

我正在测试的类中有一个与此类似的实现

private async Task<CustomObject> GetCustomObject(int id) 
{
CustomObject obj = null;
await db.ExecuteReaderAsync("nameOfProcedure", some parameters,
dr =>
{
obj = new CustomObject()
{
Prop1 = dr["Col1"],
Prop2 = dr["Col2"]
}
}
return obj;
}

我正在苦苦挣扎的是能够控制 GetCustomObject 返回的值.如果ExecuteReaderAsync实际上返回了一些我可以像这样设置的东西。

mockDataAccess.Setup(x => x.ExecuteReaderAsync("nameOfProcedure", It.IsAny<Parameters>()))
.Returns(Task.FromResult(new CustomeObject() { prop1 = "abc", prop2 = "def"};));

但是指定值的逻辑是 Action<IDataReader>我无法控制。我想知道是否有任何技巧可以用来做我想做的事,即控制 GetCustomObject 返回的对象的值.

最佳答案

看看下面的例子

[TestClass]
public class LegacyCodeTest {
[TestMethod]
public async Task TestExecuteReaderAsync() {
//Arrange
var mapping = new Dictionary<string, string> {
{ "Col1", "abc" },
{ "Col2", "def" }
};

var mockDataReader = new Mock<IDataReader>();
mockDataReader
.Setup(m => m[It.IsAny<string>()])
.Returns<string>(col => mapping[col])
.Verifiable();

var mockDataAccess = new Mock<IDataAccess>();
mockDataAccess
.Setup(m => m.ExecuteReaderAsync("nameOfProcedure", It.IsAny<Parameters>(), It.IsAny<Action<System.Data.IDataReader>[]>()))
.Returns(Task.FromResult<object>(null))
.Callback((string s, Parameters p, Action<System.Data.IDataReader>[] a) => {

if (a != null && a.Length > 0) {
a.ToList().ForEach(callback => callback(mockDataReader.Object));
}

})
.Verifiable();


var sut = new SUT(mockDataAccess.Object);

//Act
var actual = await sut.MUT(2);

//Assert
mockDataAccess.Verify();
mockDataReader.Verify(m => m["Col1"]);
mockDataReader.Verify(m => m["Col2"]);

actual.Should()
.NotBeNull()
.And
.Match<CustomObject>(c => c.Prop1 == mapping["Col1"] && c.Prop2 == mapping["Col2"]);

}

public interface IDataAccess {
Task ExecuteReaderAsync(string procedureName, Parameters procedureParameters, params Action<System.Data.IDataReader>[] actions);
}

public class Parameters { }

public class CustomObject {
public object Prop1 { get; set; }
public object Prop2 { get; set; }
}

public class SUT {
IDataAccess db;

public SUT(IDataAccess dataAccess) {
this.db = dataAccess;
}

public async Task<CustomObject> MUT(int id) {
var result = await GetCustomObject(id);
return result;
}

private async Task<CustomObject> GetCustomObject(int id) {
CustomObject obj = null;
await db.ExecuteReaderAsync("nameOfProcedure", null,
dr => {
obj = new CustomObject() {
Prop1 = dr["Col1"],
Prop2 = dr["Col2"]
};
});
return obj;
}
}
}

因为您无法控制 Action<IDataReader> ,在这种情况下,最多只能确保操作不会失败。因此,这意味着传递一个模拟阅读器,该阅读器会按照操作的预期执行。

var mapping = new Dictionary<string, string> {
{ "Col1", "abc" },
{ "Col2", "def" }
};

var mockDataReader = new Mock<IDataReader>();
mockDataReader
.Setup(m => m[It.IsAny<string>()])
.Returns<string>(col => mapping[col])
.Verifiable();

通过使用回调来访问传入的参数

.Callback((string s, Parameters p, Action<System.Data.IDataReader>[] a) => {

if (a != null && a.Length > 0) {
a.ToList().ForEach(callback => callback(mockDataReader.Object));
}

})

可以将模拟的阅读器传递给被测方法中调用的操作。

此答案是针对 OP 中提供的示例量身定制的,因此可能需要对其进行一些修改才能适用于您的特定场景。这应该足以让您应对这种情况。

关于c# - 单元测试 - 模拟的一些困难,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41811691/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com