有什么方法可以模拟结构以在单元测试下获得 class?
What are ways to mock out a struct to get a class under unit tests?
我有一个 class 正在尝试进行单元测试。 class 将结构公开为 public 属性。该结构还有一些 public 方法(比结构中的方法做的更多)。我无法更改结构(我不拥有该代码,目前风险太大)。
Mock 不适用于值类型。有没有办法有效"mock a struct"?
public class SystemUnderTest
{
public FragileStructCantChange myStruct {get; set;}
public string MethodUnderTest()
{
if(myStruct.LongRunningMethod() == "successful")
return "Ok";
else
return "Fail";
}
}
public struct FragileStructCantChange
{
public string LongRunningMethod()
{
var resultString = DoStuff(); //calls other subsystems
return resultString;
}
}
通过接口引用结构有效地将其转换为引用类型,在做出决定之前通过称为 "boxing". Boxing a struct can cause some subtle changes in behavior, which you should 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());
}
}
我不会说这些选项中的任何一个都很漂亮,在将这种后门放入共享库之前,我会认真考虑 safety/security。如果可行,接口通常更可取。
虽然这种方法可行,但我认为当您面对不想侵入式重构的对测试不友好的代码时,这可能是一个公平的折衷方案。
我有一个 class 正在尝试进行单元测试。 class 将结构公开为 public 属性。该结构还有一些 public 方法(比结构中的方法做的更多)。我无法更改结构(我不拥有该代码,目前风险太大)。
Mock 不适用于值类型。有没有办法有效"mock a struct"?
public class SystemUnderTest
{
public FragileStructCantChange myStruct {get; set;}
public string MethodUnderTest()
{
if(myStruct.LongRunningMethod() == "successful")
return "Ok";
else
return "Fail";
}
}
public struct FragileStructCantChange
{
public string LongRunningMethod()
{
var resultString = DoStuff(); //calls other subsystems
return resultString;
}
}
通过接口引用结构有效地将其转换为引用类型,在做出决定之前通过称为 "boxing". Boxing a struct can cause some subtle changes in behavior, which you should 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());
}
}
我不会说这些选项中的任何一个都很漂亮,在将这种后门放入共享库之前,我会认真考虑 safety/security。如果可行,接口通常更可取。
虽然这种方法可行,但我认为当您面对不想侵入式重构的对测试不友好的代码时,这可能是一个公平的折衷方案。