Skip to content

Commit c195433

Browse files
committed
Adding more guides
1 parent 8867397 commit c195433

File tree

9 files changed

+318
-237
lines changed

9 files changed

+318
-237
lines changed

docs/README.md

Lines changed: 28 additions & 237 deletions
Original file line numberDiff line numberDiff line change
@@ -1,248 +1,39 @@
11
# MiniMock
22

3-
MiniMock offers a _minimalistic_ approach to mocking in .NET. It is designed to be simple to use and easy to understand. It is not as feature-rich as other mocking frameworks but aims to solve __95%__ of the use cases. For the remaining __5%__, you should consider creating a custom mock.
4-
5-
MiniMock is __extremely strict__, requiring you to specify all features you want to mock. This is by design to make sure you are aware of what you are mocking. Unmocked features will throw an exception if used.
6-
7-
## Table of Contents
8-
- [Simple Example](#simple-example)
9-
- [Key Feature Summary](#key-feature-summary)
10-
- [Limitations](#limitations)
11-
- [Installation & Initialization](#installation--initialization)
12-
- [Quality of Life Features](#quality-of-life-features)
13-
- [Fluent Interface](#fluent-interface)
14-
- [Simple Return Values](#simple-return-values)
15-
- [Multiple Return Values](#multiple-return-values)
16-
- [Intercept Method Calls](#intercept-method-calls)
17-
- [Async Methods](#async-methods)
18-
- [Strict Mocking](#strict-mocking)
19-
- [Adding Indexers](#adding-indexers)
20-
- [Raising Events](#raising-events)
21-
- [Argument Matching](#argument-matching)
22-
23-
## Simple Example
24-
25-
```csharp
26-
public interface IVersionLibrary
27-
{
28-
bool DownloadExists(string version);
29-
}
30-
31-
[Fact]
32-
[Mock<IVersionLibrary>]
33-
public void SimpleExample()
34-
{
35-
var library = Mock.IVersionLibrary(config =>
36-
config.DownloadExists(returns: true));
37-
38-
var actual = library.DownloadExists("2.0.0.0");
39-
40-
Assert.True(actual);
41-
}
42-
```
43-
44-
## Key Feature Summary
45-
46-
- Minimalistic API with fluent method chaining, documentation, and full IntelliSense
47-
- Mocking of interfaces, abstract classes, and virtual methods (with limitations)
48-
- Mocking of methods, properties, indexers, and events
49-
- Simple factory methods to initialize mocks
50-
- Mocking of async methods, overloads, and generic methods
51-
- Ref and out parameters in methods supported
52-
- Generic interfaces supported
53-
54-
## Limitations
55-
56-
- No validation of calls
57-
- Only supports C# (workarounds exist for VB.NET and F#)
58-
- Ref return values and ref properties are not supported ([issue #5](https://github.com/oswaldsql/MiniMock/issues/5))
59-
- Partial mocking of classes
60-
- Base classes with constructors with parameters are not currently supported ([Issue #4](https://github.com/oswaldsql/MiniMock/issues/4))
61-
- No support for static classes or methods
62-
63-
## Installation & Initialization
64-
65-
Reference the NuGet package in your test project:
66-
67-
```sh
68-
dotnet add package MiniMock
69-
```
70-
71-
Specify which interface to mock by using the `[Mock<T>]` attribute before your test or test class:
72-
73-
```csharp
74-
[Fact]
75-
[Mock<IMyRepository>] // Specify which interface to mock
76-
public void MyTest() {
77-
var mockRepo = Mock.IMyRepository(config => config // Create a mock using the mock factory
78-
.CreateCustomerAsync(return: Guid.NewGuid()) // Configure your mock to your needs
79-
);
80-
var sut = new CustomerMaintenance(mockRepo); // Inject the mock into your system under test
81-
82-
sut.Create(customerDTO, cancellationToken);
83-
}
84-
```
85-
86-
## Quality of Life Features
87-
88-
### Fluent Interface
89-
90-
All mockable members are available through a _fluent interface_ with _IntelliSense_, _type safety_, and _documentation_.
91-
92-
Since the mock code is generated at development time, you can _inspect_, _step into_, and _debug_ the code. This also allows for _security_ and _vulnerability scanning_ of the code.
93-
94-
All code required to run MiniMock is generated and has _no runtime dependencies_.
95-
96-
### Simple Return Values
97-
98-
Simply specify what you expect returned from methods or properties. All parameters are ignored.
3+
MiniMock offers a _minimalistic_ approach to mocking in .NET with a focus on simplicity and ease of use.
994

1005
```csharp
101-
var mockLibrary = Mock.IVersionLibrary(config => config
102-
.DownloadExists(returns: true) // Returns true for any parameter
103-
.DownloadLinkAsync(returns: new Uri("http://downloads/2.0.0")) // Returns a task with a download link
104-
.CurrentVersion(value: new Version(2, 0, 0, 0)) // Sets the initial version to 2.0.0.0
105-
.Indexer(values: versions) // Provides a dictionary to retrieve and store versions
106-
);
107-
```
108-
109-
### Multiple Return Values
110-
111-
Specify multiple return values for a method or property. The first value is returned for the first call, the second for the second call, and so on.
112-
113-
```csharp
114-
var mockLibrary = Mock.IVersionLibrary(config => config
115-
.DownloadExists(returnValues: true, false, true) // Returns true, false, true for the first, second, and third call
116-
.DownloadLinkAsync(returnValues: [Task.FromResult(new Uri("http://downloads/2.0.0")), Task.FromResult(new Uri("http://downloads/2.0.1"))]) // Returns a task with a download link for the first and second call
117-
.DownloadLinkAsync(returnValues: new Uri("http://downloads/2.0.0"), new Uri("http://downloads/2.0.1")) // Returns a task with a download link for the first and second call
118-
);
119-
```
120-
121-
### Intercept Method Calls
122-
123-
```csharp
124-
[Fact]
125-
[Mock<IVersionLibrary>]
126-
public async Task InterceptMethodCalls()
127-
{
128-
var currentVersionMock = new Version(2, 0, 0);
129-
130-
var versionLibrary = Mock.IVersionLibrary(config => config
131-
.DownloadExists(call: (string s) => s.StartsWith("2.0.0") ? true : false) // Returns true for version 2.0.0.x based on a string parameter
132-
.DownloadExists(call: (Version v) => v is { Major: 2, Minor: 0, Revision: 0 }) // Returns true for version 2.0.0.x based on a version parameter
133-
// or
134-
.DownloadExists(call: LocalIntercept) // Calls a local function
135-
.DownloadExists(call: version => this.ExternalIntercept(version, true)) // Calls function in class
136-
137-
.DownloadLinkAsync(call: s => Task.FromResult(new Uri($"http://downloads/{s}"))) // Returns a task containing a download link for version 2.0.0.x otherwise an error link
138-
.DownloadLinkAsync(call: s => new Uri($"http://downloads/{s}")) // Returns a task containing a download link for version 2.0.0.x otherwise an error link
139-
140-
.CurrentVersion(get: () => currentVersionMock, set: version => currentVersionMock = version) // Overwrites the property getter and setter
141-
.Indexer(get: s => new Version(2, 0, 0, 0), set: (s, version) => {}) // Overwrites the indexer getter and setter
142-
);
143-
144-
return;
145-
146-
bool LocalIntercept(Version version)
6+
public interface IBookRepository
1477
{
148-
return version is { Major: 2, Minor: 0, Revision: 0 };
8+
Task<Guid> AddBook(Book book, CancellationToken token);
9+
int BookCount { get; set; }
10+
Book this[Guid index] { get; set; }
11+
event EventHandler<Book> NewBookAdded;
14912
}
150-
}
151-
152-
private bool ExternalIntercept(string version, bool startsWith) => startsWith ? version.StartsWith("2.0.0") : version == "2.0.0";
153-
```
154-
155-
### Async Methods
156-
157-
Simply return what you expect from async methods either as a `Task` object or a simple value.
158-
159-
```csharp
160-
var versionLibrary = Mock.IVersionLibrary(config => config
161-
.DownloadLinkAsync(returns: Task.FromResult(new Uri("http://downloads/2.0.0"))) // Returns a task containing a download link for all versions
162-
.DownloadLinkAsync(call: s => Task.FromResult(new Uri($"http://downloads/{s}"))) // Returns a task containing a download link for version 2.0.0.x otherwise an error link
163-
// or
164-
.DownloadLinkAsync(returns: new Uri("http://downloads/2.0.0")) // Returns a task containing a download link for all versions
165-
.DownloadLinkAsync(call: s => new Uri($"http://downloads/{s}")) // Returns a task containing a download link for version 2.0.0.x otherwise an error link
166-
);
167-
```
168-
169-
### Strict Mocking
170-
171-
Unmocked features will always throw `InvalidOperationException`.
172-
173-
```csharp
174-
[Fact]
175-
[Mock<IVersionLibrary>]
176-
public void UnmockedFeaturesAlwaysThrowInvalidOperationException()
177-
{
178-
var versionLibrary = Mock.IVersionLibrary();
179-
180-
var propertyException = Assert.Throws<InvalidOperationException>(() => versionLibrary.CurrentVersion);
181-
var methodException = Assert.Throws<InvalidOperationException>(() => versionLibrary.DownloadExists("2.0.0"));
182-
var asyncException = Assert.ThrowsAsync<InvalidOperationException>(() => versionLibrary.DownloadLinkAsync("2.0.0"));
183-
var indexerException = Assert.Throws<InvalidOperationException>(() => versionLibrary["2.0.0"]);
184-
}
185-
```
186-
187-
### Adding Indexers
188-
189-
Mocking indexers is supported either by overloading the get and set methods or by providing a dictionary with expected values.
19013

191-
```csharp
192-
[Fact]
193-
[Mock<IVersionLibrary>]
194-
public void Indexers()
195-
{
196-
var versions = new Dictionary<string, Version>() {{"current", new Version(2,0,0,0)}};
197-
198-
var versionLibrary = Mock.IVersionLibrary(config => config
199-
.Indexer(get: s => new Version(2,0,0,0), set: (s, version) => {}) // Overwrites the indexer getter and setter
200-
.Indexer(values: versions) // Provides a dictionary to retrieve and store versions
201-
);
202-
203-
var preCurrent = versionLibrary["current"];
204-
versionLibrary["current"] = new Version(3, 0, 0, 0);
205-
var postCurrent = versionLibrary["current"];
206-
Assert.NotEqual(preCurrent, postCurrent);
207-
}
208-
```
209-
210-
### Raising Events
211-
212-
Raise events using an event trigger.
213-
214-
```csharp
215-
Action<Version>? triggerNewVersionAdded = null;
216-
217-
var versionLibrary = Mock.IVersionLibrary(config => config
218-
.NewVersionAdded(trigger: out triggerNewVersionAdded) // Provides a trigger for when a new version is added
219-
);
220-
221-
triggerNewVersionAdded?.Invoke(new Version(2, 0, 0, 0));
222-
```
223-
224-
### Argument Matching
225-
226-
MiniMock does not support argument matching using matchers like other mocking frameworks. Instead, you can use the call parameter to match arguments using predicates or internal functions.
227-
228-
```csharp
229-
var versionLibrary = Mock.IVersionLibrary(config => config
230-
.DownloadExists(call: version => version is { Major: 2, Minor: 0 }) // Returns true for version 2.0.x based on a version parameter
231-
);
14+
[Fact]
15+
[Mock<IBookRepository>]
16+
public async Task BookCanBeCreated()
17+
{
18+
Action<Book> trigger = _ => { };
19+
20+
var mockRepo = Mock.IBookRepository(config => config
21+
.AddBook(returns: Guid.NewGuid())
22+
.BookCount(value: 10)
23+
.Indexer(values: new Dictionary<Guid, Book>())
24+
.NewBookAdded(trigger: out trigger));
25+
26+
var sut = new BookModel(mockRepo);
27+
var actual = await sut.AddBook(new Book());
28+
29+
Assert.Equal("We now have 10 books", actual);
30+
}
23231
```
23332

234-
__Using Internal Functions__
33+
Try it out or continue with [Getting started](guide/getting-started.md) to learn more or read the [Mocking guidelines](guide/mocking-guidelines.md) to get a better understanding of when, why and how to mock and when not to.
23534

236-
```csharp
237-
var versionLibrary = Mock.IVersionLibrary(config =>
238-
{
239-
bool downloadExists(Version version) => version switch {
240-
{ Major: 1, Minor: 0 } => true, // Returns true for version 1.0.x based on a version parameter
241-
{ Major: 2, Minor: 0, Revision: 0 } => true, // Returns true for version 2.0.0.0 based on a version parameter
242-
{ Major: 3, } => false, // Returns false for version 3.x based on a version parameter
243-
_ => throw new ArgumentException() // Throws an exception for all other versions
244-
};
35+
For more details on specific aspects you can read about [Methods](guide/methods.md), [Properties](guide/properties.md), [Events](guide/events.md) or
36+
[Indexers](guide/indexers.md).
37+
Or you shift into high gear with [Advanced Features](advanced-features.md), [FaQ](faq.md) or [Troubleshooting](troubleshooting.md).
24538

246-
config.DownloadExists(downloadExists);
247-
});
248-
```
39+
If you are more into the ins and outs of MiniMock you can read the [Motivation](motivation.md) behind MiniMock or the [ADRs](../ADR/README.md).

docs/guide/getting-started.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Getting started
2+
3+
## Installation and First Use
4+
5+
Reference the NuGet package in your test project:
6+
7+
```sh
8+
dotnet add package MiniMock
9+
```
10+
11+
- Specify which interface to mock by using the `[Mock<IMyRepository>]` attribute before your test or test class:
12+
- Create a new instance of the mock using the `Mock.IMyRepository()` factory method.
13+
- Configure the mock using the `config` parameter of the factory method.
14+
- Specify how the relevant members should behave using the members name and specify the behavior using the parameters.
15+
- Use the mock in your test as you see fit.
16+
17+
As code:
18+
19+
```csharp
20+
[Fact]
21+
[Mock<IMyRepository>] // Specify which interface to mock
22+
public void MyTest() {
23+
var mockRepo = Mock.IMyRepository(// Create a new instance of the mock using the mock factory
24+
config => config // Configure the mock using the config parameter
25+
.CreateCustomerAsync(return: Guid.NewGuid()) // Specify how the relevant members should behave
26+
);
27+
var sut = new CustomerMaintenance(mockRepo); // Use the mock in your test as you see fit
28+
29+
sut.Create(customerDTO, cancellationToken);
30+
}
31+
```
32+
33+
## Quality of Life
34+
35+
MiniMock is __extremely strict__ but __fair__, requiring you to specify all features you want to mock but giving you fair warnings if you don't.
36+
This is by design to make sure you are aware of what you are mocking and not introduce unexpected behaviour.
37+
38+
![img.png](img.png)
39+
40+
All mockable members are available through a _fluent interface_ with _IntelliSense_, _type safety_, and _documentation_.
41+
42+
![img_2.png](img_2.png)
43+
44+
All code required to run MiniMock is _source generated_ within your test project and has _no runtime dependencies_. You can _inspect_, _step into_, and _debug_ the generated code which also allows for _security_ and _vulnerability
45+
scanning_ of the code.
46+

docs/guide/img.png

3.55 KB
Loading

docs/guide/img_1.png

17.7 KB
Loading

docs/guide/img_2.png

17.7 KB
Loading

docs/guide/img_3.png

17.7 KB
Loading

0 commit comments

Comments
 (0)