Skip to content

Instantly share code, notes, and snippets.

@randyburden
Created October 27, 2021 17:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save randyburden/bddc1b852372ae5e7fff76282a5e7c6f to your computer and use it in GitHub Desktop.
Save randyburden/bddc1b852372ae5e7fff76282a5e7c6f to your computer and use it in GitHub Desktop.
C# Method Interception and Redirect Extension Methods and Examples using the MonoMod.RuntimeDetour library. Useful for testing hard to test code, libraries, static methods, simulating exceptions in deep call stacks of old code or libraries, etc. Keywords: C# test static methods, telerik justmock alternative, typemock alternative, typemock isolat…
using MonoMod.RuntimeDetour; // <PackageReference Include="MonoMod.RuntimeDetour" Version="21.1.11.1" />
using NUnit.Framework;
using System;
using System.Reflection;
namespace MethodInterceptionHelper
{
public static class MethodExtensions
{
/// <summary>
/// Redirect method calls from origin method to the target method.
/// This allows you to mock and test *any* method including static methods, methods in other libraries, etc.
/// Always wrap in a using statement so that the operation is undone.
/// </summary>
/// <param name="origin">Original method to swap.</param>
/// <param name="target">Target method to redirect to.</param>
public static MethodRedirectOperation RedirectTo(this MethodInfo origin, MethodInfo target)
{
return new MethodRedirectOperation(origin, target);
}
}
public class MethodRedirectOperation : IDisposable
{
private readonly Detour _detour;
public MethodRedirectOperation(MethodInfo origin, MethodInfo target)
{
_detour = new Detour(origin, target);
}
public void Dispose()
{
_detour?.Dispose();
}
}
[TestFixture]
[Explicit]
public class MethodRedirectOperationTests
{
[Test]
[Explicit]
public static void How_To_Redirect_A_Property_Get_Method()
{
// Arrange
var testObject = new TestObject();
var from = typeof(TestObject).GetProperty(nameof(TestObject.IsValid))?.GetMethod;
var to = new Func<bool>(() =>
{
// Put a breakpoint here to see that it gets called
return true;
});
using (from.RedirectTo(to.Method))
{
// Act
var value = testObject.IsValid;
// Assert
Assert.That(value, Is.True);
}
}
[Test]
[Explicit]
public static void How_To_Redirect_A_Method()
{
// Arrange
var from = typeof(MethodRedirectOperationTests).GetMethod(nameof(MethodRedirectOperationTests.GetTestObject));
var to = new Func<TestObject>(() =>
{
// Put a breakpoint here to see that it gets called
return new TestObject
{
SomeString = "A different string",
SomeInteger = 500
};
});
using (from.RedirectTo(to.Method))
{
// Act
var value = new MethodRedirectOperationTests().GetTestObject();
// Assert
Assert.That(value, Is.Not.Null);
Assert.That(value.SomeString, Is.EqualTo("A different string"));
Assert.That(value.SomeInteger, Is.EqualTo(500));
}
}
public TestObject GetTestObject()
{
return new TestObject
{
SomeString = "Some String",
SomeInteger = 1
};
}
public class TestObject
{
public string SomeString { get; set; }
public int SomeInteger { get; set; }
public bool IsValid => !string.IsNullOrWhiteSpace(SomeString) && SomeInteger != 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment