通过一个接口引用一个结构体,通过一个称为"boxing" 的过程有效地将其转换为一个引用类型。对结构进行装箱可能会导致一些细微的行为变化,您应该在做出决定之前read about。
另一种选择是在结构和被测代码之间添加一些间接性。一种方法是添加一个来指定测试值。
public class SystemUnderTest
{
public FragileStructCantChange myStruct { get; set; }
// This value can be overridden for testing purposes
public string LongRunningMethodOverride { get; set; }
public string MethodUnderTest()
{
// Call the indirect Func instead of referencing the struct directly
if (LongRunningMethod() == "successful")
return "Ok";
else
return "Fail";
}
private string LongRunningMethod()
=> LongRunningMethodOverride ?? myStruct.LongRunningMethod();
}
public class Tests
{
[Fact]
public void TestingSideDoor()
{
var sut = new SystemUnderTest();
// Override the func to supply any test data you want
sut.LongRunningMethodOverride = "successful";
Assert.Equal("Ok", sut.MethodUnderTest());
}
}
另一种方法是公开一个可覆盖的函数指针...
public class SystemUnderTest
{
public FragileStructCantChange myStruct { get; set; }
// This Func can be overridden for testing purposes
public Func<string> LongRunningMethod;
public SystemUnderTest() {
LongRunningMethod = () => myStruct.LongRunningMethod();
}
public string MethodUnderTest()
{
// Call the indirect Func instead of referencing the struct directly
if (LongRunningMethod() == "successful")
return "Ok";
else
return "Fail";
}
}
public class Tests
{
[Fact]
public void TestingSideDoor()
{
var sut = new SystemUnderTest();
// Override the func to supply any test data you want
sut.LongRunningMethod = () => "successful";
Assert.Equal("Ok", sut.MethodUnderTest());
}
}
另一种选择是使用虚拟方法,它可以由子类或模拟框架(在本例中为Moq)伪造...
public class SystemUnderTest
{
public FragileStructCantChange myStruct { get; set; }
// This method can be overridden for testing purposes
public virtual string LongRunningMethod()
=> myStruct.LongRunningMethod();
public string MethodUnderTest()
{
// Call the indirect method instead of referencing the struct directly
if (LongRunningMethod() == "successful")
return "Ok";
else
return "Fail";
}
}
public class Tests
{
[Fact]
public void TestingSideDoor()
{
var sut = new Mock<SystemUnderTest>();
// Override the method to supply any test data you want
sut.Setup(m => m.LongRunningMethod())
.Returns("successful");
Assert.Equal("Ok", sut.Object.MethodUnderTest());
}
}
我不会声称这些选项中的任何一个都很漂亮,并且在将这种后门放入共享库之前,我会认真考虑安全性。如果可行,接口通常更可取。
不过,这种方法是可行的,而且我认为当您面对不希望进行侵入式重构的测试不友好代码时,这可能是一个公平的折衷方案。