Я играл с концепцией рефакторинга статических методов для вызова делегата, который вы можете установить извне для целей тестирования.
Это не будет использовать какую-либо инфраструктуру тестирования и будет полностью сделанным на заказ решением, однако рефакторинг не повлияет на сигнатуру вашего абонента и поэтому будет относительно безопасным.
Чтобы это работало, вам необходимо иметь доступ к статическому методу, чтобы он не работал ни для каких внешних библиотек, таких как System.DateTime
.
Вот пример, с которым я играл, где я создал пару статических методов, один с возвращаемым типом, который принимает два параметра, и один универсальный, который не имеет возвращаемого типа.
Основной статический класс:
public static class LegacyStaticClass
{
// A static constructor sets up all the delegates so production keeps working as usual
static LegacyStaticClass()
{
ResetDelegates();
}
public static void ResetDelegates()
{
// All the logic that used to be in the body of the static method goes into the delegates instead.
ThrowMeDelegate = input => throw input;
SumDelegate = (a, b) => a + b;
}
public static Action<Exception> ThrowMeDelegate;
public static Func<int, int, int> SumDelegate;
public static void ThrowMe<TException>() where TException : Exception, new()
=> ThrowMeDelegate(new TException());
public static int Sum(int a, int b)
=> SumDelegate(a, b);
}
Юнит-тесты (xUnit и mustly)
public class Class1Tests : IDisposable
{
[Fact]
public void ThrowMe_NoMocking_Throws()
{
Should.Throw<Exception>(() => LegacyStaticClass.ThrowMe<Exception>());
}
[Fact]
public void ThrowMe_EmptyMocking_DoesNotThrow()
{
LegacyStaticClass.ThrowMeDelegate = input => { };
LegacyStaticClass.ThrowMe<Exception>();
true.ShouldBeTrue();
}
[Fact]
public void Sum_NoMocking_AddsValues()
{
LegacyStaticClass.Sum(5, 6).ShouldBe(11);
}
[Fact]
public void Sum_MockingReturnValue_ReturnsMockedValue()
{
LegacyStaticClass.SumDelegate = (a, b) => 6;
LegacyStaticClass.Sum(5, 6).ShouldBe(6);
}
public void Dispose()
{
LegacyStaticClass.ResetDelegates();
}
}