SimplyMock 1.5.1
dotnet add package SimplyMock --version 1.5.1
NuGet\Install-Package SimplyMock -Version 1.5.1
<PackageReference Include="SimplyMock" Version="1.5.1" />
<PackageVersion Include="SimplyMock" Version="1.5.1" />
<PackageReference Include="SimplyMock" />
paket add SimplyMock --version 1.5.1
#r "nuget: SimplyMock, 1.5.1"
#addin nuget:?package=SimplyMock&version=1.5.1
#tool nuget:?package=SimplyMock&version=1.5.1
SimplyMock
A lightweight .NET method mocking library that allows you to dynamically replace methods and properties implementation in unit tests without creating full mock objects.
Features
- Mock any method's return value with a simple API
- Support for methods with various parameter counts (0-10 parameters)
- Void method mocking support - Mock methods with no return value
- Parameter mapping mode to return predefined results based on input parameters
- Property getter mocking support
- Mock exception-throwing behavior
- Async method mocking support
- Static method and static async method mocking
- Generic method mocking support
- Extension method mocking support - Mock extension methods including void ones
- Automatic cleanup of all mocks (implements IDisposable)
Limitations
- Each method can only be mocked once, subsequent mocks will throw an error
- Methods with
in
,out
, andref
parameter modifiers are not supported
Basic Usage
1. Create IMethodMocker instance
// Create an instance in your test class constructor
private readonly IMethodMocker _methodMocker;
public YourTestClass()
{
_methodMocker = new MethodMocker();
}
// Release resources in the test class destructor
public void Dispose()
{
_methodMocker.Dispose();
}
2. Mock method return values
// Mock a parameterless method
_methodMocker.MockMethodReturn<string>(
typeof(YourClass),
"GetMessage",
() => "Mocked message"
);
// Mock a method with parameters
_methodMocker.MockMethodReturn<int, string>(
typeof(YourClass),
"GetById",
(id) => $"Mocked item {id}"
);
// Mock a multi-parameter method (up to 10 parameters supported)
_methodMocker.MockMethodReturn<string, int, bool, string>(
typeof(YourClass),
"ProcessData",
(name, code, isEnabled) => $"Processed: {name}"
);
// Example with more parameters (6 parameters)
_methodMocker.MockMethodReturn<string, int, bool, DateTime, double, char, string>(
typeof(ComplexService),
"ProcessComplexData",
(name, count, flag, date, value, code) => $"Complex processing: {name}"
);
// Example with maximum parameters (10 parameters)
_methodMocker.MockMethodReturn<string, int, bool, DateTime, double, char, byte, long, float, decimal, string>(
typeof(MaxParamService),
"ProcessMaxParams",
(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) => $"Max params processed"
);
3. Use parameter mapping (supports up to 10 parameters)
// Single parameter mapping
var resultMap = new Dictionary<string, int>
{
{ "apple", 10 },
{ "banana", 20 },
{ "orange", 15 }
};
_methodMocker.MockMethodReturnMap<string, int>(
typeof(FruitService),
"GetPrice",
resultMap
);
// Multi-parameter mapping with tuples (up to 10 parameters)
var multiParamMap = new Dictionary<(string, int, bool), string>
{
{ ("user1", 25, true), "Active user" },
{ ("user2", 30, false), "Inactive user" }
};
_methodMocker.MockMethodReturnMap<string, int, bool, string>(
typeof(UserService),
"GetUserStatus",
multiParamMap
);
// Complex mapping with many parameters
var complexMap = new Dictionary<(string, int, bool, DateTime, double, char), string>
{
{ ("data", 100, true, DateTime.Now, 3.14, 'A'), "Complex result 1" },
{ ("info", 200, false, DateTime.Today, 2.71, 'B'), "Complex result 2" }
};
_methodMocker.MockMethodReturnMap<string, int, bool, DateTime, double, char, string>(
typeof(ComplexService),
"ProcessComplexMapping",
complexMap
);
4. Mock properties
// Mock property getter
_methodMocker.MockPropertyGetter<bool>(
typeof(User),
"IsAdmin",
() => true
);
5. Mock async methods
// Mock async method that returns Task<TResult>
_methodMocker.MockAsyncMethodReturn<string>(
typeof(DataService),
"GetDataAsync", // public async Task<string> GetDataAsync()
async () => {
// Real async logic can be performed here
await Task.Delay(10);
return "Mocked async data"; // Returns string, method returns Task<string>
}
);
// Mock async method with parameters that returns Task<TResult>
_methodMocker.MockAsyncMethodReturn<int, string>(
typeof(OrderService),
"ProcessOrderAsync", // public async Task<string> ProcessOrderAsync(int orderId)
async (orderId) => {
await Task.Delay(5);
return $"Processed order {orderId}"; // Returns string, method returns Task<string>
}
);
// Mock async method that returns Task (no result - void async)
// Single parameter
_methodMocker.MockAsyncMethod<string>(
typeof(AsyncService),
"ProcessDataAsync", // public async Task ProcessDataAsync(string data)
async (data) => {
// Simulate async operation
await Task.Delay(10);
Console.WriteLine($"Async processing: {data}");
// No return value - method returns Task
}
);
// Mock async Task method with two parameters
_methodMocker.MockAsyncMethod<string, int>(
typeof(AsyncService),
"ProcessDataWithCountAsync", // public async Task ProcessDataWithCountAsync(string data, int count)
async (data, count) => {
await Task.Delay(10);
Console.WriteLine($"Processing {data} with count {count}");
}
);
// Mock async Task method with three parameters
_methodMocker.MockAsyncMethod<string, int, bool>(
typeof(AsyncService),
"ProcessUserAsync", // public async Task ProcessUserAsync(string name, int age, bool isActive)
async (name, age, isActive) => {
await Task.Delay(10);
Console.WriteLine($"Processing user: {name}, {age}, {isActive}");
}
);
// Mock async Task method with four parameters
_methodMocker.MockAsyncMethod<string, int, bool, DateTime>(
typeof(AsyncService),
"ProcessComplexDataAsync", // public async Task ProcessComplexDataAsync(string name, int count, bool flag, DateTime date)
async (name, count, flag, date) => {
await Task.Delay(10);
Console.WriteLine($"Processing complex data: {name}, {count}, {flag}, {date}");
}
);
// Mock async Task method with five parameters
_methodMocker.MockAsyncMethod<string, int, bool, DateTime, double>(
typeof(AsyncService),
"ProcessMaxParamsAsync", // public async Task ProcessMaxParamsAsync(string name, int count, bool flag, DateTime date, double value)
async (name, count, flag, date, value) => {
await Task.Delay(10);
Console.WriteLine($"Processing max params: {name}, {count}, {flag}, {date}, {value}");
}
);
6. Mock void methods
// Mock void method with parameters
_methodMocker.MockVoidMethod<string, int>(
typeof(DataProcessor),
"ProcessData",
(data, count) => {
// Capture parameters and execute custom logic
Console.WriteLine($"Processing {data} with count {count}");
}
);
// Mock extension methods (like SetReferenceType)
_methodMocker.MockVoidMethod<ICommunicationInternalManager, string>(
typeof(CommunicationExtensions),
"SetReferenceType",
(manager, referenceType) => {
// Mock the extension method behavior
Console.WriteLine($"SetReferenceType called with: {referenceType}");
}
);
// Mock void method to throw exceptions
_methodMocker.MockVoidMethod<string>(
typeof(FileService),
"DeleteFile",
(filePath) => {
throw new FileNotFoundException($"File not found: {filePath}");
}
);
// Mock async Task methods (void async methods)
_methodMocker.MockAsyncMethod<string>(
typeof(AsyncService),
"ProcessDataAsync",
async (data) => {
// Simulate async operation
await Task.Delay(10);
Console.WriteLine($"Async processing: {data}");
// Can also throw exceptions or perform other logic
}
);
7. Mock extension methods
// Define an extension method
public static class EnumExtensions
{
public static string GetEnumDisplayName(this Enum value)
{
return value.ToString();
}
}
public enum TestStatus
{
Active,
Inactive,
Pending,
Completed
}
// Mock the extension method
_methodMocker.MockMethodReturn<Enum, string>(
typeof(EnumExtensions),
"GetEnumDisplayName",
(enumValue) => $"Mocked-{enumValue}"
);
// Use the mocked extension method
var result = TestStatus.Active.GetEnumDisplayName();
// result = "Mocked-Active"
// Extension method parameter mapping
var enumDisplayMap = new Dictionary<Enum, string>
{
{ TestStatus.Active, "活跃" },
{ TestStatus.Inactive, "非活跃" },
{ TestStatus.Pending, "待处理" },
{ TestStatus.Completed, "已完成" }
};
_methodMocker.MockMethodReturnMap<Enum, string>(
typeof(EnumExtensions),
"GetEnumDisplayName",
enumDisplayMap
);
// Test different enum values
Assert.Equal("活跃", TestStatus.Active.GetEnumDisplayName());
Assert.Equal("非活跃", TestStatus.Inactive.GetEnumDisplayName());
// Extension method with complex logic
_methodMocker.MockMethodReturn<Enum, string>(
typeof(EnumExtensions),
"GetEnumDisplayName",
(enumValue) =>
{
return enumValue switch
{
TestStatus.Active => "🟢 Active",
TestStatus.Inactive => "🔴 Inactive",
TestStatus.Pending => "🟡 Pending",
TestStatus.Completed => "✅ Completed",
_ => "❓ Unknown"
};
}
);
8. Mock generic methods
// Mock a generic static method with no parameters
// Generic method: public static T Get<T>()
_methodMocker.MockGenericStaticMethodReturn<Person>(
typeof(ServiceFactory),
"Get",
() => new Person {
Name = "John Doe",
Age = 30
}
);
// Mock a generic static method with one parameter
// Generic method: public static T Map<T>(object source)
_methodMocker.MockGenericStaticMethodReturn<UserDto, object>(
typeof(Mapper),
"Map",
src => new UserDto {
Id = 1001,
Username = "johndoe",
Email = "john@example.com"
}
);
// Using the mocked generic methods
Person person = ServiceFactory.Get<Person>();
UserDto userDto = Mapper.Map<UserDto>(sourceObject);
8. Dynamically control static generic method return values with an external variable
Sometimes you may want to flexibly change the return value of a static generic method during your test. You can achieve this by combining an external static variable with a mock:
// Define a static class to control the return value externally
// The MockReturn<T> class now automatically uses the type's full name as the key
// This allows different types to have separate instances without manual naming
public static class MockReturn<T>
{
private static readonly Dictionary<string, T> _instances = new();
private static readonly object _lock = new();
/// <summary>
/// Gets the automatic name based on the type's full name
/// </summary>
private static string GetTypeName()
{
return typeof(T).FullName ?? typeof(T).Name;
}
public static T GetInstance(string name)
{
lock (_lock)
{
_instances.TryGetValue(name, out var instance);
return instance;
}
}
public static void SetInstance(string name, T instance)
{
lock (_lock)
{
_instances[name] = instance;
}
}
/// <summary>
/// Gets instance using the type's full name as the key
/// This automatically separates different types without manual naming
/// </summary>
public static T GetInstance()
{
return GetInstance(GetTypeName());
}
/// <summary>
/// Sets instance using the type's full name as the key
/// This automatically separates different types without manual naming
/// </summary>
public static void SetInstance(T instance)
{
SetInstance(GetTypeName(), instance);
}
public static IEnumerable<string> GetAllNames()
{
lock (_lock)
{
return _instances.Keys.ToList();
}
}
}
// Example static generic method
public static class StaticProvider
{
public static T Get<T>() => default;
}
public class StaticMethodMock_ExternalVariable_Tests : IDisposable
{
private readonly IMethodMocker _methodMocker;
public StaticMethodMock_ExternalVariable_Tests()
{
_methodMocker = new MethodMocker();
}
public void Dispose()
{
_methodMocker.Dispose();
}
[Fact]
public void Mock_StaticGenericMethod_Returns_ExternalVariable()
{
// Arrange - Now you can set instances for different types automatically
var expectedString = "Hello, Mock!";
var expectedInt = 42;
// Each type automatically gets its own instance based on type name
MockReturn<string>.SetInstance(expectedString);
MockReturn<int>.SetInstance(expectedInt);
// Mock the static generic method to return the external variable
_methodMocker.MockGenericStaticMethodReturn<string>(
typeof(StaticProvider),
"Get",
() => MockReturn<string>.GetInstance()
);
_methodMocker.MockGenericStaticMethodReturn<int>(
typeof(StaticProvider),
"Get",
() => MockReturn<int>.GetInstance()
);
// Act & Assert - Different types return their respective values
Assert.Equal("Hello, Mock!", StaticProvider.Get<string>());
Assert.Equal(42, StaticProvider.Get<int>());
// Change the external variable and call again
MockReturn<string>.SetInstance("Changed!");
MockReturn<int>.SetInstance(100);
Assert.Equal("Changed!", StaticProvider.Get<string>());
Assert.Equal(100, StaticProvider.Get<int>());
}
[Fact]
public void Mock_StaticGenericMethod_SupportsNamedInstances()
{
// You can still use named instances for more complex scenarios
MockReturn<string>.SetInstance("scenario1", "First scenario");
MockReturn<string>.SetInstance("scenario2", "Second scenario");
// Mock different behaviors based on named instances
_methodMocker.MockGenericStaticMethodReturn<string>(
typeof(StaticProvider),
"Get",
() => MockReturn<string>.GetInstance("scenario1")
);
Assert.Equal("First scenario", StaticProvider.Get<string>());
// Switch to different scenario
_methodMocker.MockGenericStaticMethodReturn<string>(
typeof(StaticProvider),
"Get",
() => MockReturn<string>.GetInstance("scenario2")
);
Assert.Equal("Second scenario", StaticProvider.Get<string>());
}
}
Advanced Usage
Mock exceptions
// Mock method to throw exceptions
_methodMocker.MockMethodReturn<string, string>(
typeof(UserService),
"GetUserName",
(userId) => {
if (string.IsNullOrEmpty(userId))
throw new ArgumentNullException(nameof(userId));
if (userId == "invalid")
throw new InvalidOperationException("User not found");
return $"User {userId}";
}
);
Notes
- Ensure you call
Dispose()
after tests to release all mocks - When mocking methods, parameter types and count must match exactly
- When using
MockMethodReturnMap
, undefined parameter combinations will throwKeyNotFoundException
- Async method mocking supports real async operations, you can use
await
in replacement functions - SimplyMock supports methods with up to 5 parameters, for both sync and async methods
- Static methods and static async methods are mocked the same way as instance methods
- Generic static methods can be mocked by providing a Func delegate directly
- Multiple mocks on the same method will override previous implementations
- SimplyMock modifies method behavior at runtime, making it ideal for testing legacy code or third-party libraries
- Void method mocking allows you to mock methods with no return value, including extension methods
- Extension method mocking: For extension methods, the first parameter in the mock should be the
this
parameter type - Void method parameter capture: Use Action delegates to capture method parameters and execute custom logic
- Async Task method mocking: Use
MockAsyncMethod
to mock methods that returnTask
(void async methods) - these support real async operations withawait
Complete Example
using System;
using Xunit;
using SimplyMock;
public class CalculatorService
{
public int Add(int a, int b) => a + b;
}
public class CalculatorTests : IDisposable
{
private readonly IMethodMocker _methodMocker;
public CalculatorTests()
{
_methodMocker = new MethodMocker();
}
public void Dispose()
{
_methodMocker.Dispose();
}
[Fact]
public void Add_ShouldReturnMockedResult()
{
// Replace the implementation of Add method
_methodMocker.MockMethodReturn<int, int, int>(
typeof(CalculatorService),
"Add",
(a, b) => a * b // Replace with multiplication
);
var calculator = new CalculatorService();
int result = calculator.Add(5, 3);
// Now Add method performs multiplication
Assert.Equal(15, result);
}
}
Void Method Complete Example
using System;
using Xunit;
using SimplyMock;
public interface ICommunicationManager
{
// Interface definition
}
public class TestCommunicationManager : ICommunicationManager
{
// Implementation
}
public static class CommunicationExtensions
{
public static void SetReferenceType(this ICommunicationManager manager, string referenceType)
{
// Original extension method implementation
Console.WriteLine($"Original SetReferenceType: {referenceType}");
}
}
public class VoidMethodTests : IDisposable
{
private readonly IMethodMocker _methodMocker;
public VoidMethodTests()
{
_methodMocker = new MethodMocker();
}
public void Dispose()
{
_methodMocker.Dispose();
}
[Fact]
public void SetReferenceType_ShouldBeMocked()
{
// Arrange - Mock the extension method
string capturedReferenceType = null;
ICommunicationManager capturedManager = null;
_methodMocker.MockVoidMethod<ICommunicationManager, string>(
typeof(CommunicationExtensions),
nameof(CommunicationExtensions.SetReferenceType),
(manager, referenceType) =>
{
capturedManager = manager;
capturedReferenceType = referenceType;
Console.WriteLine($"Mocked SetReferenceType: {referenceType}");
});
var manager = new TestCommunicationManager();
// Act - Call the mocked extension method
manager.SetReferenceType("TestReference");
// Assert - Verify the mock was called
Assert.Same(manager, capturedManager);
Assert.Equal("TestReference", capturedReferenceType);
}
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- Lib.Harmony (>= 2.3.6)
- MonoMod.RuntimeDetour.HookGen (>= 22.7.31.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version 1.5.1:
change Lib.Harmony nuget Version to 2.3.6:
Version 1.5.0:
- Extended parameter support: All mock methods now support up to 10 parameters (previously 5)
- Added MockMethodReturn, MockMethodReturnMap, MockVoidMethod, MockAsyncMethodReturn, and MockAsyncMethod overloads for 6-10 parameters
- Modular architecture with partial classes for better maintainability
- 100% backward compatible - no breaking changes
- Comprehensive test coverage with 27 new test cases