Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 14 additions & 17 deletions Docs/pages/00-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,25 @@

[![Nuget](https://img.shields.io/nuget/v/Mockolate)](https://www.nuget.org/packages/Mockolate)

**Mockolate** is a modern, strongly-typed mocking library for .NET, powered by source generators. It enables fast,
compile-time validated mocks for interfaces and classes, supporting .NET Standard 2.0, .NET 8, .NET 10, and .NET
Framework 4.8.
**Mockolate** is a modern, strongly-typed, AOT-compatible mocking library for .NET, powered by source generators.
It enables fast, compile-time validated mocking with .NET Standard 2.0, .NET 8, .NET 10 and .NET Framework 4.8.

- **Source generator-based**: No runtime proxy generation, fast and reliable.
- **Strongly-typed**: Setup and verify mocks with full IntelliSense and compile-time safety.
- **Source generator-based**: No runtime proxy generation.
- **Strongly-typed**: Compile-time safety and IntelliSense support.
- **AOT compatible**: Works with NativeAOT and trimming.

## Getting Started

1. Check prerequisites
Although Mockolate supports multiple .NET Standard 2.0 compatible frameworks (including .NET Framework 4.8), you must
have the [.NET 10 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) installed to build and compile
Mockolate. This is required because Mockolate
leverages [C# 14 extension members](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods#declare-extension-members).
1. **Check prerequisites**
Install the [.NET 10 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/10.0), because Mockolate leverages
C# 14 extension members (the projects can still target any supported framework).

2. Install the [`Mockolate`](https://www.nuget.org/packages/Mockolate) nuget package
```ps
2. **Install the package**
```powershell
dotnet add package Mockolate
```

3. Create and use the mock
3. **Create and use a mock**
```csharp
using Mockolate;

Expand All @@ -39,15 +36,15 @@ Framework 4.8.
}

// Create a mock for IChocolateDispenser
var sut = Mock.Create<IChocolateDispenser>();
IChocolateDispenser sut = Mock.Create<IChocolateDispenser>();

// Setup: Initial stock of 10 for Dark chocolate
sut.SetupMock.Indexer(It.Is("Dark")).InitializeWith(10);
// Setup: Dispense decreases Dark chocolate if enough, returns true/false
sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny<int>())
.Returns((type, amount) =>
{
var current = sut[type];
int current = sut[type];
if (current >= amount)
{
sut[type] = current - amount;
Expand All @@ -59,10 +56,10 @@ Framework 4.8.

// Track dispensed amount via event
int dispensedAmount = 0;
sut.ChocolateDispensed += (type, amount)
sut.ChocolateDispensed += (type, amount) =>
{
dispensedAmount += amount;
}
};

// Act: Try to dispense chocolates
bool gotChoc1 = sut.Dispense("Dark", 4); // true
Expand Down
44 changes: 25 additions & 19 deletions Docs/pages/01-create-mocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@ You can create mocks for interfaces and classes. For classes without a default c

```csharp
// Create a mock for an interface
var sut = Mock.Create<IChocolateDispenser>();
IChocolateDispenser sut = Mock.Create<IChocolateDispenser>();

// Create a mock for a class
var classMock = Mock.Create<MyChocolateDispenser>();
MyChocolateDispenser classMock = Mock.Create<MyChocolateDispenser>();

// For classes without a default constructor:
var classWithArgsMock = Mock.Create<MyChocolateDispenserWithCtor>(
MyChocolateDispenserWithCtor classWithArgsMock = Mock.Create<MyChocolateDispenserWithCtor>(
BaseClass.WithConstructorParameters("Dark", 42)
);
```

You can specify up to eight additional interfaces that the mock also implements (beyond the first type):

// Specify up to 8 additional interfaces for the mock:
var sut2 = Mock.Create<MyChocolateDispenser, ILemonadeDispenser>();
```csharp
// return type is a MyChocolateDispenser that also implements ILemonadeDispenser
var sut = Mock.Create<MyChocolateDispenser, ILemonadeDispenser>();
```

**Notes:**
Expand All @@ -29,7 +33,7 @@ var sut2 = Mock.Create<MyChocolateDispenser, ILemonadeDispenser>();
You can control the default behavior of the mock by providing a `MockBehavior`:

```csharp
var strictMock = Mock.Create<IChocolateDispenser>(new MockBehavior { ThrowWhenNotSetup = true });
var strictMock = Mock.Create<IChocolateDispenser>(MockBehavior.Default with { ThrowWhenNotSetup = true });

// For classes with constructor parameters and custom behavior:
var classMock = Mock.Create<MyChocolateDispenser>(
Expand All @@ -40,18 +44,13 @@ var classMock = Mock.Create<MyChocolateDispenser>(

### `MockBehavior` options

- `ThrowWhenNotSetup` (bool):
- If `false` (default), the mock will return a default value (see `DefaultValue`).
- If `true`, the mock will throw an exception when a method or property is called without a setup.
- `SkipBaseClass` (bool):
- If `false` (default), the mock will call the base class implementation and use its return values as default
values, if no explicit setup is defined.
- If `true`, the mock will not call any base class implementations.
- `Initialize<T>(params Action<IMockSetup<T>>[] setups)`:
- Automatically initialize all mocks of type T with the given setups when they are created.
- The callback can optionally receive an additional counter parameter which allows to differentiate between multiple
instances. This is useful when you want to ensure that you can distinguish between different automatically created
instances.
- `ThrowWhenNotSetup` (bool):
- If `false` (default), the mock will return a default value (see `DefaultValue`), when no matching setup is found.
- If `true`, the mock will throw an exception when no matching setup is found.
- `DefaultValue` (IDefaultValueGenerator):
- Customizes how default values are generated for methods/properties that are not set up.
- The default implementation provides sensible defaults for the most common use cases:
Expand All @@ -60,16 +59,22 @@ var classMock = Mock.Create<MyChocolateDispenser>(
- Completed tasks for `Task`, `Task<T>`, `ValueTask` and `ValueTask<T>`
- Tuples with recursively defaulted values
- `null` for other reference types
- You can provide custom default values for specific types using `.WithDefaultValueFor<T>()`:
- You can add custom default value factories for specific types using `.WithDefaultValueFor<T>()`:
```csharp
var behavior = MockBehavior.Default
.WithDefaultValueFor<string>(() => "default")
.WithDefaultValueFor<int>(() => 42);
var sut = Mock.Create<IChocolateDispenser>(behavior);
```
This is useful when you want mocks to return specific default values for certain types instead of the standard
defaults (e.g., `null`, `0`, empty strings).
- `.UseConstructorParametersFor<T>(object?[])`:
defaults.
- `Initialize<T>(params Action<IMockSetup<T>>[] setups)`:
- Automatically initialize all mocks of type T with the given setups when they are created.
- The callback can optionally receive an additional counter parameter, allowing you to differentiate between multiple
automatically created instances.
For example, when initializing `IDbConnection` mocks, you can use the counter to assign different database names or
connection strings to each mock so they can be verified independently.
- `UseConstructorParametersFor<T>(object?[])`:
- Configures constructor parameters to use when creating mocks of type `T`, unless explicit parameters are provided
during mock creation via `BaseClass.WithConstructorParameters(…)`.

Expand All @@ -78,7 +83,7 @@ var classMock = Mock.Create<MyChocolateDispenser>(
Use `Mock.Factory` to create multiple mocks with a shared behavior:

```csharp
var behavior = new MockBehavior { ThrowWhenNotSetup = true };
var behavior = MockBehavior.Default with { ThrowWhenNotSetup = true };
var factory = new Mock.Factory(behavior);

var sut1 = factory.Create<IChocolateDispenser>();
Expand All @@ -94,7 +99,7 @@ You can wrap an existing instance with mock tracking using `Mock.Wrap<T>()`. Thi
a real object:

```csharp
var realDispenser = new ChocolateDispenser();
var realDispenser = new MyChocolateDispenser();
var wrappedDispenser = Mock.Wrap<IChocolateDispenser>(realDispenser);

// Calls are forwarded to the real instance
Expand All @@ -109,4 +114,5 @@ wrappedDispenser.VerifyMock.Invoked.Dispense(It.Is("Dark"), It.Is(5)).Once();
- Only interface types can be wrapped with `Mock.Wrap<T>()`.
- All calls are forwarded to the wrapped instance.
- You can still set up custom behavior that overrides the wrapped instance's behavior.
- You cannot override protected members of the wrapped instance.
- Verification works the same as with regular mocks.
75 changes: 39 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,25 @@
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=aweXpect_Mockolate&metric=coverage)](https://sonarcloud.io/summary/overall?id=aweXpect_Mockolate)
[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2FaweXpect%2FMockolate%2Fmain)](https://dashboard.stryker-mutator.io/reports/github.com/aweXpect/Mockolate/main)

**Mockolate** is a modern, strongly-typed, AOT-compatible mocking library for .NET, powered by source generators. It
enables fast, compile-time validated mocks for interfaces and classes, supporting .NET Standard 2.0, .NET 8, .NET 10,
and .NET Framework 4.8.
**Mockolate** is a modern, strongly-typed, AOT-compatible mocking library for .NET, powered by source generators.
It enables fast, compile-time validated mocking with .NET Standard 2.0, .NET 8, .NET 10 and .NET Framework 4.8.

- **Source generator-based**: No runtime proxy generation, fast and reliable.
- **Strongly-typed**: Setup and verify mocks with full IntelliSense and compile-time safety.
- **Source generator-based**: No runtime proxy generation.
- **Strongly-typed**: Compile-time safety and IntelliSense support.
- **AOT compatible**: Works with NativeAOT and trimming.

## Getting Started

1. Check prerequisites
Although Mockolate supports multiple .NET Standard 2.0 compatible frameworks (including .NET Framework 4.8), you must
have the [.NET 10 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) installed to build and compile
Mockolate. This is required because Mockolate
leverages [C# 14 extension members](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods#declare-extension-members).
1. **Check prerequisites**
Install the [.NET 10 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/10.0), because Mockolate leverages
C# 14 extension members (the projects can still target any supported framework).

2. Install the [`Mockolate`](https://www.nuget.org/packages/Mockolate) nuget package
```ps
2. **Install the package**
```powershell
dotnet add package Mockolate
```

3. Create and use the mock
3. **Create and use a mock**
```csharp
using Mockolate;

Expand All @@ -43,15 +40,15 @@ and .NET Framework 4.8.
}

// Create a mock for IChocolateDispenser
var sut = Mock.Create<IChocolateDispenser>();
IChocolateDispenser sut = Mock.Create<IChocolateDispenser>();

// Setup: Initial stock of 10 for Dark chocolate
sut.SetupMock.Indexer(It.Is("Dark")).InitializeWith(10);
// Setup: Dispense decreases Dark chocolate if enough, returns true/false
sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny<int>())
.Returns((type, amount) =>
{
var current = sut[type];
int current = sut[type];
if (current >= amount)
{
sut[type] = current - amount;
Expand All @@ -63,10 +60,10 @@ and .NET Framework 4.8.

// Track dispensed amount via event
int dispensedAmount = 0;
sut.ChocolateDispensed += (type, amount)
sut.ChocolateDispensed += (type, amount) =>
{
dispensedAmount += amount;
}
};

// Act: Try to dispense chocolates
bool gotChoc1 = sut.Dispense("Dark", 4); // true
Expand All @@ -87,18 +84,22 @@ You can create mocks for interfaces and classes. For classes without a default c

```csharp
// Create a mock for an interface
var sut = Mock.Create<IChocolateDispenser>();
IChocolateDispenser sut = Mock.Create<IChocolateDispenser>();

// Create a mock for a class
var classMock = Mock.Create<MyChocolateDispenser>();
MyChocolateDispenser classMock = Mock.Create<MyChocolateDispenser>();

// For classes without a default constructor:
var classWithArgsMock = Mock.Create<MyChocolateDispenserWithCtor>(
MyChocolateDispenserWithCtor classWithArgsMock = Mock.Create<MyChocolateDispenserWithCtor>(
BaseClass.WithConstructorParameters("Dark", 42)
);
```

// Specify up to 8 additional interfaces for the mock:
var sut2 = Mock.Create<MyChocolateDispenser, ILemonadeDispenser>();
You can specify up to eight additional interfaces that the mock also implements (beyond the first type):

```csharp
// return type is a MyChocolateDispenser that also implements ILemonadeDispenser
var sut = Mock.Create<MyChocolateDispenser, ILemonadeDispenser>();
```

**Notes:**
Expand All @@ -111,7 +112,7 @@ var sut2 = Mock.Create<MyChocolateDispenser, ILemonadeDispenser>();
You can control the default behavior of the mock by providing a `MockBehavior`:

Comment thread
vbreuss marked this conversation as resolved.
```csharp
var strictMock = Mock.Create<IChocolateDispenser>(new MockBehavior { ThrowWhenNotSetup = true });
var strictMock = Mock.Create<IChocolateDispenser>(MockBehavior.Default with { ThrowWhenNotSetup = true });

// For classes with constructor parameters and custom behavior:
var classMock = Mock.Create<MyChocolateDispenser>(
Expand All @@ -122,18 +123,13 @@ var classMock = Mock.Create<MyChocolateDispenser>(

**`MockBehavior` options**

- `ThrowWhenNotSetup` (bool):
- If `false` (default), the mock will return a default value (see `DefaultValue`).
- If `true`, the mock will throw an exception when a method or property is called without a setup.
- `SkipBaseClass` (bool):
- If `false` (default), the mock will call the base class implementation and use its return values as default
values, if no explicit setup is defined.
- If `true`, the mock will not call any base class implementations.
- `Initialize<T>(params Action<IMockSetup<T>>[] setups)`:
- Automatically initialize all mocks of type T with the given setups when they are created.
- The callback can optionally receive an additional counter parameter which allows to differentiate between multiple
instances. This is useful when you want to ensure that you can distinguish between different automatically created
instances.
- `ThrowWhenNotSetup` (bool):
- If `false` (default), the mock will return a default value (see `DefaultValue`), when no matching setup is found.
- If `true`, the mock will throw an exception when no matching setup is found.
- `DefaultValue` (IDefaultValueGenerator):
- Customizes how default values are generated for methods/properties that are not set up.
- The default implementation provides sensible defaults for the most common use cases:
Expand All @@ -142,16 +138,22 @@ var classMock = Mock.Create<MyChocolateDispenser>(
- Completed tasks for `Task`, `Task<T>`, `ValueTask` and `ValueTask<T>`
- Tuples with recursively defaulted values
- `null` for other reference types
- You can provide custom default values for specific types using `.WithDefaultValueFor<T>()`:
- You can add custom default value factories for specific types using `.WithDefaultValueFor<T>()`:
```csharp
var behavior = MockBehavior.Default
.WithDefaultValueFor<string>(() => "default")
.WithDefaultValueFor<int>(() => 42);
var sut = Mock.Create<IChocolateDispenser>(behavior);
```
This is useful when you want mocks to return specific default values for certain types instead of the standard
defaults (e.g., `null`, `0`, empty strings).
- `.UseConstructorParametersFor<T>(object?[])`:
defaults.
- `Initialize<T>(params Action<IMockSetup<T>>[] setups)`:
- Automatically initialize all mocks of type T with the given setups when they are created.
- The callback can optionally receive an additional counter parameter, allowing you to differentiate between multiple
automatically created instances.
For example, when initializing `IDbConnection` mocks, you can use the counter to assign different database names or
connection strings to each mock so they can be verified independently.
- `UseConstructorParametersFor<T>(object?[])`:
- Configures constructor parameters to use when creating mocks of type `T`, unless explicit parameters are provided
during mock creation via `BaseClass.WithConstructorParameters(…)`.

Expand All @@ -160,7 +162,7 @@ var classMock = Mock.Create<MyChocolateDispenser>(
Use `Mock.Factory` to create multiple mocks with a shared behavior:

```csharp
var behavior = new MockBehavior { ThrowWhenNotSetup = true };
var behavior = MockBehavior.Default with { ThrowWhenNotSetup = true };
var factory = new Mock.Factory(behavior);

var sut1 = factory.Create<IChocolateDispenser>();
Expand All @@ -176,7 +178,7 @@ You can wrap an existing instance with mock tracking using `Mock.Wrap<T>()`. Thi
a real object:

```csharp
var realDispenser = new ChocolateDispenser();
var realDispenser = new MyChocolateDispenser();
var wrappedDispenser = Mock.Wrap<IChocolateDispenser>(realDispenser);

// Calls are forwarded to the real instance
Expand All @@ -191,6 +193,7 @@ wrappedDispenser.VerifyMock.Invoked.Dispense(It.Is("Dark"), It.Is(5)).Once();
- Only interface types can be wrapped with `Mock.Wrap<T>()`.
- All calls are forwarded to the wrapped instance.
- You can still set up custom behavior that overrides the wrapped instance's behavior.
- You cannot override protected members of the wrapped instance.
- Verification works the same as with regular mocks.

## Setup
Expand Down
Loading