Skip to content

Commit 962e1bb

Browse files
authored
Merge pull request #41 from twsouthwick/substitute-provide
Add overload to SubstituteForBuilder<> that allows configuration of substitute with access to Autofac container
2 parents 1e03574 + 24f5ad8 commit 962e1bb

File tree

5 files changed

+103
-10
lines changed

5 files changed

+103
-10
lines changed

AutofacContrib.NSubstitute.Tests/ExampleFixture.cs

+28
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Autofac;
22
using NSubstitute;
3+
using NSubstitute.Extensions;
34
using NUnit.Framework;
45

56
namespace AutofacContrib.NSubstitute.Tests
@@ -41,6 +42,14 @@ public int AMethod()
4142
}
4243
}
4344

45+
public class ConcreteClassWithObject
46+
{
47+
public virtual object GetResult()
48+
{
49+
return new object();
50+
}
51+
}
52+
4453
public class ConcreteClass
4554
{
4655
private readonly int _i;
@@ -175,6 +184,25 @@ public void Example_test_with_substitute_for_concrete()
175184
Assert.That(result, Is.EqualTo(val3));
176185
}
177186

187+
[Test]
188+
public void SubstituteForConfigureWithContext()
189+
{
190+
const int val = 2;
191+
192+
using var utoSubstitute = AutoSubstitute.Configure()
193+
.SubstituteFor<ConcreteClass>(val).Configured()
194+
.SubstituteFor<ConcreteClassWithObject>().Configure((s, ctx) =>
195+
{
196+
s.Configure().GetResult().Returns(ctx.Resolve<ConcreteClass>());
197+
})
198+
.Build()
199+
.Container;
200+
201+
var result = utoSubstitute.Resolve<ConcreteClassWithObject>().GetResult();
202+
203+
Assert.AreSame(result, utoSubstitute.Resolve<ConcreteClass>());
204+
}
205+
178206
[Test]
179207
public void Example_test_with_substitute_for_concrete_resolved_from_autofac()
180208
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using NSubstitute.Extensions;
2+
3+
namespace AutofacContrib.NSubstitute.Tests
4+
{
5+
internal static class NSubstituteExtensions
6+
{
7+
public static void AssertIsNSubstituteMock(this object obj)
8+
{
9+
// This throws an exception if it is not an NSubstitute mock
10+
obj.Configure();
11+
}
12+
}
13+
}

AutofacContrib.NSubstitute/AutoSubstituteBuilder.cs

+14-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace AutofacContrib.NSubstitute
99
{
1010
public class AutoSubstituteBuilder
1111
{
12+
private readonly Dictionary<Type, object> _substituteForRegistrations = new Dictionary<Type, object>();
1213
private readonly List<IProvidedValue> _providedValues;
1314
private readonly ContainerBuilder _builder;
1415

@@ -53,6 +54,7 @@ public AutoSubstituteBuilder ConfigureBuilder(Action<ContainerBuilder> action)
5354
/// </summary>
5455
/// <typeparam name="TService">The type to register the implementation as</typeparam>
5556
/// <typeparam name="TImplementation">The implementation type</typeparam>
57+
/// <param name="providedValue">Parameter to obtain a provided value.</param>
5658
/// <param name="parameters">Optional constructor parameters</param>
5759
/// <returns>The current <see cref="AutoSubstituteBuilder"/>.</returns>
5860
public AutoSubstituteBuilder Provide<TService, TImplementation>(out IProvidedValue<TService> providedValue, params Parameter[] parameters)
@@ -112,11 +114,19 @@ public AutoSubstituteBuilder Provide<TService>(TService instance, object service
112114
public SubstituteForBuilder<TService> SubstituteFor<TService>(params object[] parameters)
113115
where TService : class
114116
{
115-
var substitute = Substitute.For<TService>(parameters);
117+
if (_substituteForRegistrations.TryGetValue(typeof(TService), out var result))
118+
{
119+
return (SubstituteForBuilder<TService>)result;
120+
}
121+
122+
var registration = _builder.Register(_ => Substitute.For<TService>(parameters))
123+
.As<TService>()
124+
.InstancePerLifetimeScope();
125+
var builder = new SubstituteForBuilder<TService>(this, registration);
116126

117-
Provide(substitute);
127+
_substituteForRegistrations.Add(typeof(TService), builder);
118128

119-
return new SubstituteForBuilder<TService>(this, substitute);
129+
return builder;
120130
}
121131

122132
/// <summary>
@@ -136,7 +146,7 @@ public AutoSubstituteBuilder ResolveAndSubstituteFor<TService>(params Parameter[
136146
return this;
137147
}
138148

139-
private IProvidedValue<TService> CreateProvidedValue<TService>(Func<IContainer, TService> factory)
149+
internal IProvidedValue<TService> CreateProvidedValue<TService>(Func<IContainer, TService> factory)
140150
{
141151
var value = new ProvidedValue<TService>(factory);
142152

Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System;
1+
using Autofac;
2+
using Autofac.Builder;
3+
using System;
24

35
namespace AutofacContrib.NSubstitute
46
{
@@ -10,12 +12,12 @@ public class SubstituteForBuilder<TService>
1012
where TService : class
1113
{
1214
private readonly AutoSubstituteBuilder _builder;
13-
private readonly TService _service;
15+
private readonly IRegistrationBuilder<TService, SimpleActivatorData, SingleRegistrationStyle> _registration;
1416

15-
internal SubstituteForBuilder(AutoSubstituteBuilder builder, TService service)
17+
internal SubstituteForBuilder(AutoSubstituteBuilder builder, IRegistrationBuilder<TService, SimpleActivatorData, SingleRegistrationStyle> registration)
1618
{
1719
_builder = builder;
18-
_service = service;
20+
_registration = registration;
1921
}
2022

2123
/// <summary>
@@ -24,10 +26,27 @@ internal SubstituteForBuilder(AutoSubstituteBuilder builder, TService service)
2426
/// <param name="action">The delegate to configure the service.</param>
2527
/// <returns>The original <see cref="AutoSubstituteBuilder"/>.</returns>
2628
public AutoSubstituteBuilder Configure(Action<TService> action)
29+
=> Configure((s, _) => action(s));
30+
31+
/// <summary>
32+
/// Allows for configuration of the service with access to the resolved components.
33+
/// </summary>
34+
/// <param name="action">The delegate to configure the service.</param>
35+
/// <returns>The original <see cref="AutoSubstituteBuilder"/>.</returns>
36+
public AutoSubstituteBuilder Configure(Action<TService, IComponentContext> action)
2737
{
28-
action(_service);
38+
_registration.OnActivated(args =>
39+
{
40+
action(args.Instance, args.Context);
41+
});
42+
2943
return _builder;
3044
}
31-
}
3245

46+
/// <summary>
47+
/// Completes the configuration of the substitute.
48+
/// </summary>
49+
/// <returns>The original <see cref="AutoSubstituteBuilder"/>.</returns>
50+
public AutoSubstituteBuilder Configured() => _builder;
51+
}
3352
}

README.md

+23
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,29 @@ public void Example_test_with_substitute_for_concrete()
164164
}
165165
```
166166

167+
If you want to configure it with a service from the container, you can use a separate overload:
168+
169+
```c#
170+
[Test]
171+
public void SubstituteForConfigureWithContext()
172+
{
173+
const int val = 2;
174+
175+
using var utoSubstitute = AutoSubstitute.Configure()
176+
.SubstituteFor<ConcreteClass>(val).Configured()
177+
.SubstituteFor<ConcreteClassWithObject>().Configure((s, ctx) =>
178+
{
179+
s.Configure().GetResult().Returns(ctx.Resolve<ConcreteClass>());
180+
})
181+
.Build()
182+
.Container;
183+
184+
var result = utoSubstitute.Resolve<ConcreteClassWithObject>().GetResult();
185+
186+
Assert.AreSame(result, utoSubstitute.Resolve<ConcreteClass>());
187+
}
188+
```
189+
167190
Similarly, you can resolve a concrete type from the autosubstitute container and register that with the underlying container using the `ResolveAndSubstituteFor` method:
168191

169192
```c#

0 commit comments

Comments
 (0)