Skip to content

Commit b175d28

Browse files
authored
Allow customization of which mocks to generate (#45)
1 parent 9b2c921 commit b175d28

File tree

6 files changed

+106
-3
lines changed

6 files changed

+106
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using Autofac.Core.Registration;
2+
using NUnit.Framework;
3+
using System.Collections.Generic;
4+
5+
namespace AutofacContrib.NSubstitute.Tests
6+
{
7+
[TestFixture]
8+
public class TypesToSkipForMockingFixture
9+
{
10+
[Test]
11+
public void WithoutOption()
12+
{
13+
var mock = AutoSubstitute.Configure()
14+
.Provide<IDependency, Impl1>(out var impl1)
15+
.Provide<IDependency, Impl2>(out var impl2)
16+
.Build();
17+
18+
var items = mock.Resolve<IDependency[]>();
19+
20+
Assert.AreEqual(impl1.Value, items[0]);
21+
Assert.AreEqual(impl2.Value, items[1]);
22+
Assert.That(items[2], Is.NSubstituteMock);
23+
}
24+
25+
[Test]
26+
public void ManuallyAddTypeToSkip()
27+
{
28+
var mock = AutoSubstitute.Configure()
29+
.ConfigureOptions(options =>
30+
{
31+
options.TypesToSkipForMocking.Add(typeof(IDependency));
32+
})
33+
.Build();
34+
35+
Assert.Throws<ComponentNotRegisteredException>(() => mock.Resolve<IDependency>());
36+
}
37+
38+
[Test]
39+
public void DisableMockGenerationOnProvide()
40+
{
41+
var mock = AutoSubstitute.Configure()
42+
.ConfigureOptions(options =>
43+
{
44+
options.AutomaticallySkipMocksForProvidedValues = true;
45+
})
46+
.Provide<IDependency, Impl1>(out var impl1)
47+
.Provide<IDependency, Impl2>(out var impl2)
48+
.Build();
49+
50+
var items = mock.Resolve<IEnumerable<IDependency>>();
51+
52+
CollectionAssert.AreEqual(items, new[] { impl1.Value, impl2.Value });
53+
}
54+
55+
public interface IDependency
56+
{
57+
}
58+
59+
public class Impl1 : IDependency
60+
{
61+
}
62+
63+
public class Impl2 : IDependency
64+
{
65+
}
66+
}
67+
}

AutofacContrib.NSubstitute/AutoSubstituteBuilder.cs

+12
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ public AutoSubstituteBuilder Provide<TService, TImplementation>(out IProvidedVal
9191

9292
providedValue = CreateProvidedValue<TService>(c => c.ResolveKeyed<TService>(key));
9393

94+
SkipMockIfNeeded<TService>();
95+
9496
return this;
9597
}
9698

@@ -105,6 +107,8 @@ public AutoSubstituteBuilder Provide<TService>(TService instance)
105107
{
106108
_builder.RegisterInstance(instance);
107109

110+
SkipMockIfNeeded<TService>();
111+
108112
return this;
109113
}
110114

@@ -164,6 +168,14 @@ public AutoSubstituteBuilder ResolveAndSubstituteFor<TService>(params Parameter[
164168
return this;
165169
}
166170

171+
private void SkipMockIfNeeded<T>()
172+
{
173+
if (_options.AutomaticallySkipMocksForProvidedValues)
174+
{
175+
_options.TypesToSkipForMocking.Add(typeof(T));
176+
}
177+
}
178+
167179
private SubstituteForBuilder<TService> CreateSubstituteForBuilder<TService>(Func<TService> factory, bool isSubstituteFor)
168180
where TService : class
169181
{

AutofacContrib.NSubstitute/AutoSubstituteOptions.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ public class AutoSubstituteOptions
1616
/// <summary>
1717
/// Gets a collection of delegates that can augment the registrations of objects created but not registered.
1818
/// </summary>
19-
public ICollection<Action<IRegistrationBuilder<object, ConcreteReflectionActivatorData, SingleRegistrationStyle>>> ConfigureAnyConcreteTypeRegistration { get; } = new List<Action<IRegistrationBuilder<object, ConcreteReflectionActivatorData, SingleRegistrationStyle>>>();
19+
public ICollection<Action<IRegistrationBuilder<object, ConcreteReflectionActivatorData, SingleRegistrationStyle>>> ConfigureAnyConcreteTypeRegistration { get; } = new List<Action<IRegistrationBuilder<object, ConcreteReflectionActivatorData, SingleRegistrationStyle>>>();
20+
21+
/// <summary>
22+
/// Gets a collection of types that will be skipped during generation of NSubstitute mocks.
23+
/// </summary>
24+
public ICollection<Type> TypesToSkipForMocking { get; } = new HashSet<Type>();
25+
26+
/// <summary>
27+
/// Gets or sets a flag indicating whether mocks should be excluded for provided values. This will automatically add values given to Provide methods to <see cref="TypesToSkipForMocking"/>.
28+
/// </summary>
29+
public bool AutomaticallySkipMocksForProvidedValues { get; set; }
2030
}
2131
}

AutofacContrib.NSubstitute/NSubstituteRegistrationHandler.cs

+9-1
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,12 @@ public IEnumerable<IComponentRegistration> RegistrationsFor
4646
(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
4747
{
4848
if (service == null)
49-
throw new ArgumentNullException("service");
49+
{
50+
throw new ArgumentNullException(nameof(service));
51+
}
5052

5153
var typedService = service as IServiceWithType;
54+
5255
if (typedService == null ||
5356
!typedService.ServiceType.IsInterface ||
5457
IsGenericListOrCollectionInterface(typedService.ServiceType) ||
@@ -57,6 +60,11 @@ public IEnumerable<IComponentRegistration> RegistrationsFor
5760
service is DecoratorService)
5861
return Enumerable.Empty<IComponentRegistration>();
5962

63+
if (_options.TypesToSkipForMocking.Contains(typedService.ServiceType))
64+
{
65+
return Enumerable.Empty<IComponentRegistration>();
66+
}
67+
6068
var rb = RegistrationBuilder
6169
.ForDelegate((c, p) =>
6270
{

GitVersionConfig.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
mode: ContinuousDelivery
2-
next-version: 6.0.0
2+
next-version: 6.1.0
33
branches: {}

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ public void Example_test_with_concrete_object_provide()
144144
}
145145
```
146146

147+
### NSubstitute Helpers
148+
147149
There is also a convenient syntax for registering and resolving a `Substitute.For<T>()` with the underlying container for concrete classes:
148150

149151
```c#
@@ -318,6 +320,8 @@ public void Example_test_with_substitute_for_concrete_resolved_from_autofac()
318320
}
319321
```
320322

323+
### Direct configuration with ContainerBuilder
324+
321325
If you need to access the underlying Autofac container for some reason then you use the `Container` property on the `AutoSubstitute` object.
322326

323327
If you want to make modifications to the container builder before the container is build from it there is a second constructor parameter you can use, for example:
@@ -333,6 +337,8 @@ There are various options that can be used to set up the container and NSubstitu
333337

334338
- Add custom `MockHandler` instances that can intercept the creation of the NSubstitute mocks
335339
- Add custom handlers for the registration of implicit service creation via `AnyConcreteTypeNotAlreadyRegisteredSource`
340+
- Add types that will be skipped during the mock generation via `TypesToSkipForMocking` option
341+
- Automatically skip mock generation of types that are specified in Provide methods via `AutomaticallySkipMocksForProvidedValues`
336342

337343
Some convenience methods build upon this to enable a few common scenarios:
338344

0 commit comments

Comments
 (0)