I have a class that I'm trying to get under unit tests. The class exposes a struct as a public property. The struct also has some public methods (that do much more than a method in a struct should). I can't make changes to the struct (I don't own that code and it would be too risky at the moment).
Mock doesn't work on value types. Is there a way to effectively "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;
}
}
Referencing a struct through an interface effectively turns it into a reference type, through a process called "boxing". Boxing a struct can cause some subtle changes in behavior, which you should read about before making your decision.
Another option is to add some indirection between the struct and the code under test. One way is to add a to specify a test value.
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());
}
}
Another way is to expose an overridable function pointer...
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());
}
}
Another option is to use a virtual method, which can be faked either by a subclass or a mocking framework (Moq, in this example)...
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());
}
}
I won't claim that either of these options is pretty, and I'd think hard about safety/security before putting this sort of backdoor into a shared library. An interface is usually preferable, when viable.
This sort of thing works, though, and I think it can be a fair compromise when you're faced with test-unfriendly code that you don't want to invasively refactor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With