Skip to content

Commit 1e03574

Browse files
authoredAug 25, 2020
Merge pull request #39 from twsouthwick/builder
Update to Autofac 5.2.0 and react to immutability changes
2 parents a00e862 + 49f0d2c commit 1e03574

15 files changed

+391
-219
lines changed
 

‎AutofacContrib.NSubstitute.Tests/AutoSubstituteCollectionFixture.cs

+46-45
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections.Generic;
22
using System.Linq;
33
using System.Security.Cryptography.X509Certificates;
4+
using Autofac;
45
using NSubstitute;
56
using NUnit.Framework;
67

@@ -78,76 +79,76 @@ public TestIReadOnlyListComponent(IReadOnlyList<IServiceItem> serviceItems)
7879
[Test]
7980
public void TestIEnumerableCorrectlyResolves()
8081
{
81-
using(var autosub = new AutoSubstitute())
82-
{
83-
var mockA = autosub.Provide<IServiceItem, ServiceItemA>();
84-
var mockB = autosub.Provide<IServiceItem, ServiceItemB>();
85-
var component = autosub.Resolve<TestIEnumerableComponent>();
82+
using var autosub = AutoSubstitute.Configure()
83+
.Provide<IServiceItem, ServiceItemA>(out var mockA)
84+
.Provide<IServiceItem, ServiceItemB>(out var mockB)
85+
.Build();
8686

87-
Assert.That(component.ServiceItems, Is.Not.Empty);
88-
Assert.That(component.ServiceItems.Contains(mockA), Is.True);
89-
Assert.That(component.ServiceItems.Contains(mockB), Is.True);
90-
}
87+
var component = autosub.Resolve<TestIEnumerableComponent>();
88+
89+
Assert.That(component.ServiceItems, Is.Not.Empty);
90+
Assert.That(component.ServiceItems.Contains(mockA.Value), Is.True);
91+
Assert.That(component.ServiceItems.Contains(mockB.Value), Is.True);
9192
}
9293

9394
[Test]
9495
public void TestIListCorrectlyResolves()
9596
{
96-
using(var autosub = new AutoSubstitute())
97-
{
98-
var mockA = autosub.Provide<IServiceItem, ServiceItemA>();
99-
var mockB = autosub.Provide<IServiceItem, ServiceItemB>();
100-
var component = autosub.Resolve<TestIListComponent>();
97+
using var autosub = AutoSubstitute.Configure()
98+
.Provide<IServiceItem, ServiceItemA>(out var mockA)
99+
.Provide<IServiceItem, ServiceItemB>(out var mockB)
100+
.Build();
101101

102-
Assert.That(component.ServiceItems, Is.Not.Empty);
103-
Assert.That(component.ServiceItems.Contains(mockA), Is.True);
104-
Assert.That(component.ServiceItems.Contains(mockB), Is.True);
105-
}
102+
var component = autosub.Resolve<TestIListComponent>();
103+
104+
Assert.That(component.ServiceItems, Is.Not.Empty);
105+
Assert.That(component.ServiceItems.Contains(mockA.Value), Is.True);
106+
Assert.That(component.ServiceItems.Contains(mockB.Value), Is.True);
106107
}
107108

108109
[Test]
109110
public void TestIReadOnlyCollectionCorrectlyResolves()
110111
{
111-
using(var autosub = new AutoSubstitute())
112-
{
113-
var mockA = autosub.Provide<IServiceItem, ServiceItemA>();
114-
var mockB = autosub.Provide<IServiceItem, ServiceItemB>();
115-
var component = autosub.Resolve<TestIReadOnlyCollectionComponent>();
112+
using var autosub = AutoSubstitute.Configure()
113+
.Provide<IServiceItem, ServiceItemA>(out var mockA)
114+
.Provide<IServiceItem, ServiceItemB>(out var mockB)
115+
.Build();
116116

117-
Assert.That(component.ServiceItems, Is.Not.Empty);
118-
Assert.That(component.ServiceItems.Contains(mockA), Is.True);
119-
Assert.That(component.ServiceItems.Contains(mockB), Is.True);
120-
}
117+
var component = autosub.Resolve<TestIReadOnlyCollectionComponent>();
118+
119+
Assert.That(component.ServiceItems, Is.Not.Empty);
120+
Assert.That(component.ServiceItems.Contains(mockA.Value), Is.True);
121+
Assert.That(component.ServiceItems.Contains(mockB.Value), Is.True);
121122
}
122123

123124
[Test]
124125
public void TestICollectionCorrectlyResolves()
125126
{
126-
using(var autosub = new AutoSubstitute())
127-
{
128-
var mockA = autosub.Provide<IServiceItem, ServiceItemA>();
129-
var mockB = autosub.Provide<IServiceItem, ServiceItemB>();
130-
var component = autosub.Resolve<TestICollectionComponent>();
127+
using var autosub = AutoSubstitute.Configure()
128+
.Provide<IServiceItem, ServiceItemA>(out var mockA)
129+
.Provide<IServiceItem, ServiceItemB>(out var mockB)
130+
.Build();
131131

132-
Assert.That(component.ServiceItems, Is.Not.Empty);
133-
Assert.That(component.ServiceItems.Contains(mockA), Is.True);
134-
Assert.That(component.ServiceItems.Contains(mockB), Is.True);
135-
}
132+
var component = autosub.Resolve<TestICollectionComponent>();
133+
134+
Assert.That(component.ServiceItems, Is.Not.Empty);
135+
Assert.That(component.ServiceItems.Contains(mockA.Value), Is.True);
136+
Assert.That(component.ServiceItems.Contains(mockB.Value), Is.True);
136137
}
137138

138139
[Test]
139140
public void TestIReadOnlyListCorrectlyResolves()
140141
{
141-
using(var autosub = new AutoSubstitute())
142-
{
143-
var mockA = autosub.Provide<IServiceItem, ServiceItemA>();
144-
var mockB = autosub.Provide<IServiceItem, ServiceItemB>();
145-
var component = autosub.Resolve<TestIReadOnlyListComponent>();
142+
using var autosub = AutoSubstitute.Configure()
143+
.Provide<IServiceItem, ServiceItemA>(out var mockA)
144+
.Provide<IServiceItem, ServiceItemB>(out var mockB)
145+
.Build();
146146

147-
Assert.That(component.ServiceItems, Is.Not.Empty);
148-
Assert.That(component.ServiceItems.Contains(mockA), Is.True);
149-
Assert.That(component.ServiceItems.Contains(mockB), Is.True);
150-
}
147+
var component = autosub.Resolve<TestIReadOnlyListComponent>();
148+
149+
Assert.That(component.ServiceItems, Is.Not.Empty);
150+
Assert.That(component.ServiceItems.Contains(mockA.Value), Is.True);
151+
Assert.That(component.ServiceItems.Contains(mockB.Value), Is.True);
151152
}
152153
}
153154
}

‎AutofacContrib.NSubstitute.Tests/AutoSubstituteFixture.cs

+27-31
Original file line numberDiff line numberDiff line change
@@ -49,70 +49,66 @@ public void RunAll()
4949
[Test]
5050
public void DefaultConstructorIsLoose()
5151
{
52-
using (var mock = new AutoSubstitute())
53-
{
54-
RunWithSingleSetupationTest(mock);
55-
}
52+
using var mock = new AutoSubstitute();
53+
54+
RunWithSingleSetupationTest(mock);
5655
}
5756

5857
[Test]
5958
public void ProvideMock()
6059
{
61-
using (var autoSubstitute = new AutoSubstitute())
62-
{
63-
var mockA = Substitute.For<IServiceA>();
64-
autoSubstitute.Provide(mockA);
60+
var mockA = Substitute.For<IServiceA>();
6561

66-
var component = autoSubstitute.Resolve<TestComponent>();
67-
component.RunAll();
62+
using var autoSubstitute = AutoSubstitute.Configure()
63+
.Provide(mockA)
64+
.Build();
6865

69-
mockA.Received().RunA();
70-
}
66+
var component = autoSubstitute.Resolve<TestComponent>();
67+
component.RunAll();
68+
69+
mockA.Received().RunA();
7170
}
7271

7372
[Test]
7473
public void ProvideImplementation()
7574
{
76-
using (var mock = new AutoSubstitute())
77-
{
78-
var serviceA = mock.Provide<IServiceA, ServiceA>();
75+
using var mock = AutoSubstitute.Configure()
76+
.Provide<IServiceA, ServiceA>(out var serviceA)
77+
.Build();
7978

80-
Assert.IsNotNull(serviceA);
81-
Assert.IsFalse(serviceA is ICallRouter);
82-
}
79+
Assert.IsNotNull(serviceA.Value);
80+
Assert.IsFalse(serviceA.Value is ICallRouter);
8381
}
8482

8583
[Test]
8684
public void DefaultConstructorWorksWithAllTests()
8785
{
88-
using (var mock = new AutoSubstitute())
89-
{
90-
RunTest(mock);
91-
}
86+
using var mock = new AutoSubstitute();
87+
88+
RunTest(mock);
9289
}
9390

9491
[Test]
9592
public void WorksWithUnmetSetupations()
9693
{
97-
using (var loose = new AutoSubstitute())
98-
{
99-
RunWithSingleSetupationTest(loose);
100-
}
94+
using var loose = new AutoSubstitute();
95+
96+
RunWithSingleSetupationTest(loose);
10197
}
10298

10399
[Test]
104100
public void NormalSetupationsAreVerified()
105101
{
106-
using (var mock = new AutoSubstitute())
107-
{
108-
Assert.That(() => SetUpSetupations(mock), Throws.TypeOf<ReceivedCallsException>());
109-
}
102+
using var mock = new AutoSubstitute();
103+
104+
Assert.That(() => SetUpSetupations(mock), Throws.TypeOf<ReceivedCallsException>());
110105
}
111106

112107
[Test]
113108
public void ProperInitializationIsPerformed()
114109
{
115-
var autoSubstitute = new AutoSubstitute();
110+
using var autoSubstitute = new AutoSubstitute();
111+
116112
Assert.IsNotNull(autoSubstitute.Container);
117113
}
118114

‎AutofacContrib.NSubstitute.Tests/AutofacContrib.NSubstitute.Tests.csproj

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp2.2</TargetFramework>
4+
<TargetFramework>netcoreapp3.1</TargetFramework>
55

66
<IsPackable>false</IsPackable>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Autofac" Version="4.9.4" />
11-
<PackageReference Include="NSubstitute" Version="4.2.1" />
10+
<PackageReference Include="Autofac" Version="5.2.0" />
11+
<PackageReference Include="NSubstitute" Version="4.2.2" />
1212
<PackageReference Include="nunit" Version="3.12.0" />
13-
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
14-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
13+
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
1515
</ItemGroup>
1616

1717
<ItemGroup>

‎AutofacContrib.NSubstitute.Tests/ExampleFixture.cs

+32-22
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public ConcreteClassWithDependency(IDependency1 dependency, int i)
6969

7070
public int Double()
7171
{
72-
return _dependency.SomeMethod(_i)*2;
72+
return _dependency.SomeMethod(_i) * 2;
7373
}
7474
}
7575

@@ -127,12 +127,15 @@ public void Example_test_with_standard_resolve()
127127
public void Example_test_with_concrete_type_provided()
128128
{
129129
const int val = 3;
130-
var AutoSubstitute = new AutoSubstitute();
131-
AutoSubstitute.Resolve<IDependency2>().SomeOtherMethod().Returns(val); // This shouldn't do anything because of the next line
132-
AutoSubstitute.Provide<IDependency2, Dependency2>();
133-
AutoSubstitute.Resolve<IDependency1>().SomeMethod(Arg.Any<int>()).Returns(c => c.Arg<int>());
134130

135-
var result = AutoSubstitute.Resolve<MyClass>().AMethod();
131+
using var mock = AutoSubstitute.Configure()
132+
.Provide<IDependency2, Dependency2>(out _)
133+
.Build();
134+
135+
mock.Resolve<IDependency2>().SomeOtherMethod().Returns(val); // This shouldn't do anything because of the next line
136+
mock.Resolve<IDependency1>().SomeMethod(Arg.Any<int>()).Returns(c => c.Arg<int>());
137+
138+
var result = mock.Resolve<MyClass>().AMethod();
136139

137140
Assert.That(result, Is.EqualTo(Dependency2.Value));
138141
}
@@ -142,11 +145,14 @@ public void Example_test_with_concrete_object_provided()
142145
{
143146
const int val1 = 3;
144147
const int val2 = 2;
145-
var AutoSubstitute = new AutoSubstitute();
146-
AutoSubstitute.Resolve<IDependency2>().SomeOtherMethod().Returns(val1);
147-
AutoSubstitute.Provide(new ConcreteClass(val2));
148148

149-
var result = AutoSubstitute.Resolve<MyClassWithConcreteDependency>().AMethod();
149+
var mock = AutoSubstitute.Configure()
150+
.Provide(new ConcreteClass(val2))
151+
.Build();
152+
153+
mock.Resolve<IDependency2>().SomeOtherMethod().Returns(val1);
154+
155+
var result = mock.Resolve<MyClassWithConcreteDependency>().AMethod();
150156

151157
Assert.That(result, Is.EqualTo(val1 + val2));
152158
}
@@ -157,11 +163,14 @@ public void Example_test_with_substitute_for_concrete()
157163
const int val1 = 3;
158164
const int val2 = 2;
159165
const int val3 = 10;
160-
var AutoSubstitute = new AutoSubstitute();
161-
AutoSubstitute.Resolve<IDependency2>().SomeOtherMethod().Returns(val1);
162-
AutoSubstitute.SubstituteFor<ConcreteClass>(val2).Add(Arg.Any<int>()).Returns(val3);
163166

164-
var result = AutoSubstitute.Resolve<MyClassWithConcreteDependency>().AMethod();
167+
using var utoSubstitute = AutoSubstitute.Configure()
168+
.SubstituteFor<ConcreteClass>(val2).Configure(c => c.Add(Arg.Any<int>()).Returns(val3))
169+
.Build();
170+
171+
utoSubstitute.Resolve<IDependency2>().SomeOtherMethod().Returns(val1);
172+
173+
var result = utoSubstitute.Resolve<MyClassWithConcreteDependency>().AMethod();
165174

166175
Assert.That(result, Is.EqualTo(val3));
167176
}
@@ -172,16 +181,17 @@ public void Example_test_with_substitute_for_concrete_resolved_from_autofac()
172181
const int val1 = 2;
173182
const int val2 = 3;
174183
const int val3 = 4;
175-
var AutoSubstitute = new AutoSubstitute();
176-
// Much better / more maintainable than:
177-
//AutoSubstitute.SubstituteFor<ConcreteClassWithDependency>(AutoSubstitute.Resolve<IDependency1>(), val1);
178-
AutoSubstitute.ResolveAndSubstituteFor<ConcreteClassWithDependency>(new TypedParameter(typeof(int), val1));
179-
AutoSubstitute.Resolve<IDependency2>().SomeOtherMethod().Returns(val2);
180-
AutoSubstitute.Resolve<IDependency1>().SomeMethod(val1).Returns(val3);
181184

182-
var result = AutoSubstitute.Resolve<MyClassWithConcreteDependencyThatHasDependencies>().AMethod();
185+
using var mock = AutoSubstitute.Configure()
186+
.ResolveAndSubstituteFor<ConcreteClassWithDependency>(new TypedParameter(typeof(int), val1))
187+
.Build();
188+
189+
mock.Resolve<IDependency2>().SomeOtherMethod().Returns(val2);
190+
mock.Resolve<IDependency1>().SomeMethod(val1).Returns(val3);
191+
192+
var result = mock.Resolve<MyClassWithConcreteDependencyThatHasDependencies>().AMethod();
183193

184-
Assert.That(result, Is.EqualTo(val2*val3*2));
194+
Assert.That(result, Is.EqualTo(val2 * val3 * 2));
185195
}
186196
}
187197
}

‎AutofacContrib.NSubstitute.Tests/KeyedRegistrationFixture.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,14 @@ public static void ShouldResolveASubstituteForIndexedDependency()
5050
[Test]
5151
public static void ShouldAcceptProvidedIndexedDependency()
5252
{
53-
var autoSubstitute = new AutoSubstitute();
5453
var substitute = Substitute.For<IDependency2>();
54+
55+
using var autoSubstitute = AutoSubstitute.Configure()
56+
.Provide(substitute, Switch.On)
57+
.Build();
58+
5559
substitute.SomeOtherMethod().Returns(5);
56-
autoSubstitute.Provide(substitute, Switch.On);
57-
60+
5861
var target = autoSubstitute.Resolve<ClassWithKeyedDependencies>();
5962

6063
Assert.That(target.OnDependency.SomeOtherMethod(), Is.EqualTo(5));

‎AutofacContrib.NSubstitute.sln

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ VisualStudioVersion = 16.0.29409.12
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutofacContrib.NSubstitute", "AutofacContrib.NSubstitute\AutofacContrib.NSubstitute.csproj", "{7E69ECB4-84A5-41F0-8497-D94A3A45757F}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutofacContrib.NSubstitute.Tests", "AutofacContrib.NSubstitute.Tests\AutofacContrib.NSubstitute.Tests.csproj", "{56FF83E0-BCB9-4391-9BCC-912347C84398}"
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutofacContrib.NSubstitute.Tests", "AutofacContrib.NSubstitute.Tests\AutofacContrib.NSubstitute.Tests.csproj", "{56FF83E0-BCB9-4391-9BCC-912347C84398}"
99
EndProject
1010
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A5ECD18F-1D42-444E-BB3A-6062D8B0E256}"
1111
ProjectSection(SolutionItems) = preProject
1212
BREAKING_CHANGES.md = BREAKING_CHANGES.md
13+
global.json = global.json
1314
LICENSE = LICENSE
1415
logo.png = logo.png
1516
README.md = README.md
+20-98
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,38 @@
1-
using System;
2-
using Autofac;
3-
using Autofac.Builder;
1+
using Autofac;
42
using Autofac.Core;
5-
using Autofac.Features.ResolveAnything;
6-
using NSubstitute;
3+
using System;
74

85
namespace AutofacContrib.NSubstitute
96
{
107
/// <summary>
118
/// Deprecated auto mocking container. Use <see cref="AutoSubstitute"/> instead.
129
/// </summary>
1310
[Obsolete("AutoMock has been deprecated in favour of AutoSubstitute.")]
14-
public class AutoMock : AutoSubstitute {}
11+
public class AutoMock : AutoSubstitute { }
1512

1613
/// <summary>
1714
/// Auto mocking container using <see cref="Autofac"/> and <see cref="NSubstitute"/>.
1815
/// </summary>
19-
public class AutoSubstitute: IDisposable
16+
public class AutoSubstitute : IDisposable
2017
{
18+
/// <summary>
19+
/// Creates a builder to configure the mocks and dependencies.
20+
/// </summary>
21+
/// <returns>An instance of <see cref="AutoSubstituteBuilder"/>.</returns>
22+
public static AutoSubstituteBuilder Configure() => new AutoSubstituteBuilder();
23+
2124
/// <summary>
2225
/// <see cref="IContainer"/> that handles the component resolution.
2326
/// </summary>
24-
public IContainer Container { get; private set; }
27+
public IContainer Container { get; }
2528

2629
/// <summary>
2730
/// Create an AutoSubstitute.
2831
/// </summary>
2932
public AutoSubstitute()
3033
{
31-
var builder = new ContainerBuilder();
32-
33-
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
34-
builder.RegisterSource(new NSubstituteRegistrationHandler());
35-
36-
Container = builder.Build();
34+
Container = Configure()
35+
.InternalBuild();
3736
}
3837

3938
/// <summary>
@@ -42,14 +41,14 @@ public AutoSubstitute()
4241
/// <param name="builderModifier">Action to modify the <see cref="Autofac.ContainerBuilder"/></param>
4342
public AutoSubstitute(Action<ContainerBuilder> builderModifier)
4443
{
45-
var builder = new ContainerBuilder();
46-
47-
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
48-
builder.RegisterSource(new NSubstituteRegistrationHandler());
49-
50-
builderModifier(builder);
44+
Container = Configure()
45+
.ConfigureBuilder(builderModifier)
46+
.InternalBuild();
47+
}
5148

52-
Container = builder.Build();
49+
internal AutoSubstitute(IContainer container)
50+
{
51+
Container = container;
5352
}
5453

5554
/// <summary>
@@ -70,82 +69,5 @@ public T Resolve<T>(params Parameter[] parameters)
7069
{
7170
return Container.Resolve<T>(parameters);
7271
}
73-
74-
/// <summary>
75-
/// Register the specified implementation type to the container as the specified service type and resolve it using the given parameters.
76-
/// </summary>
77-
/// <typeparam name="TService">The type to register the implementation as</typeparam>
78-
/// <typeparam name="TImplementation">The implementation type</typeparam>
79-
/// <param name="parameters">Optional constructor parameters</param>
80-
/// <returns>The resolved service instance</returns>
81-
public TService Provide<TService, TImplementation>(params Parameter[] parameters)
82-
{
83-
Container.ComponentRegistry.Register(RegistrationBuilder.ForType<TImplementation>()
84-
.As<TService>().InstancePerLifetimeScope().CreateRegistration()
85-
);
86-
87-
return Container.Resolve<TService>(parameters);
88-
}
89-
90-
/// <summary>
91-
/// Register the specified object to the container as the specified service type and resolve it.
92-
/// </summary>
93-
/// <typeparam name="TService">The type to register the object as</typeparam>
94-
/// <param name="instance">The object to register into the container</param>
95-
/// <returns>The instance resolved from container</returns>
96-
public TService Provide<TService>(TService instance)
97-
where TService : class
98-
{
99-
Container.ComponentRegistry.Register(RegistrationBuilder.ForDelegate((c, p) => instance)
100-
.InstancePerLifetimeScope().CreateRegistration()
101-
);
102-
103-
return Container.Resolve<TService>();
104-
}
105-
106-
/// <summary>
107-
/// Register the specified object to the container as the specified keyed service type and resolve it.
108-
/// </summary>
109-
/// <typeparam name="TService">The type to register the object as</typeparam>
110-
/// <param name="instance">The object to register into the container</param>
111-
/// <param name="serviceKey">The key to register the service with</param>
112-
/// <returns>The instance resolved from container</returns>
113-
public TService Provide<TService>(TService instance, object serviceKey)
114-
where TService : class
115-
{
116-
Container.ComponentRegistry.Register(RegistrationBuilder.ForDelegate((c, p) => instance).As(new KeyedService(serviceKey, typeof(TService)))
117-
.InstancePerLifetimeScope().CreateRegistration()
118-
);
119-
120-
return Container.Resolve<TService>();
121-
}
122-
123-
/// <summary>
124-
/// Registers to the container and returns a substitute for a given concrete class given the explicit constructor parameters.
125-
/// This is used for concrete classes where NSubstitutes won't be created by default by the container when using Resolve.
126-
/// For advanced uses consider using directly <see cref="Substitute.For{TService}"/> and then calling <see cref="Provide{TService}(TService)"/> so that type is used on dependencies for other Resolved types.
127-
/// </summary>
128-
/// <typeparam name="TService">The type to register and return a substitute for</typeparam>
129-
/// <param name="parameters">Optional constructor parameters</param>
130-
/// <returns>The instance resolved from the container</returns>
131-
public TService SubstituteFor<TService>(params object[] parameters) where TService : class
132-
{
133-
var substitute = Substitute.For<TService>(parameters);
134-
return Provide(substitute);
135-
}
136-
137-
/// <summary>
138-
/// Registers to the container and returns a substitute for a given concrete class using autofac to resolve the constructor parameters.
139-
/// This is used for concrete classes where NSubstitutes won't be created by default by the container when using Resolve.
140-
/// For advanced uses consider using directly <see cref="Substitute.For{TService}"/> and then calling <see cref="Provide{TService}(TService)"/> so that type is used on dependencies for other Resolved types.
141-
/// </summary>
142-
/// <typeparam name="TService">The type to register and return a substitute for</typeparam>
143-
/// <param name="parameters">Any constructor parameters that Autofac can't resolve automatically</param>
144-
/// <returns>The instance resolved from the container</returns>
145-
public TService ResolveAndSubstituteFor<TService>(params Parameter[] parameters) where TService : class
146-
{
147-
var substitute = Resolve<TService>(parameters);
148-
return Provide(substitute);
149-
}
15072
}
15173
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
using Autofac;
2+
using Autofac.Core;
3+
using Autofac.Features.ResolveAnything;
4+
using NSubstitute;
5+
using System;
6+
using System.Collections.Generic;
7+
8+
namespace AutofacContrib.NSubstitute
9+
{
10+
public class AutoSubstituteBuilder
11+
{
12+
private readonly List<IProvidedValue> _providedValues;
13+
private readonly ContainerBuilder _builder;
14+
15+
public AutoSubstituteBuilder()
16+
{
17+
_builder = new ContainerBuilder();
18+
_providedValues = new List<IProvidedValue>();
19+
}
20+
21+
public AutoSubstitute Build()
22+
=> new AutoSubstitute(InternalBuild());
23+
24+
internal IContainer InternalBuild()
25+
{
26+
_builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
27+
_builder.RegisterSource(new NSubstituteRegistrationHandler());
28+
29+
var container = _builder.Build();
30+
31+
foreach (var provided in _providedValues)
32+
{
33+
provided.SetContainer(container);
34+
}
35+
36+
return container;
37+
}
38+
39+
/// <summary>
40+
/// Provides direct access to the <see cref="ContainerBuilder"/> to manipulate how needed.
41+
/// </summary>
42+
/// <param name="action">A delegate to run to configure the builder.</param>
43+
/// <returns>The current <see cref="AutoSubstituteBuilder"/>.</returns>
44+
public AutoSubstituteBuilder ConfigureBuilder(Action<ContainerBuilder> action)
45+
{
46+
action(_builder);
47+
48+
return this;
49+
}
50+
51+
/// <summary>
52+
/// Register the specified implementation type to the container as the specified service type and resolve it using the given parameters.
53+
/// </summary>
54+
/// <typeparam name="TService">The type to register the implementation as</typeparam>
55+
/// <typeparam name="TImplementation">The implementation type</typeparam>
56+
/// <param name="parameters">Optional constructor parameters</param>
57+
/// <returns>The current <see cref="AutoSubstituteBuilder"/>.</returns>
58+
public AutoSubstituteBuilder Provide<TService, TImplementation>(out IProvidedValue<TService> providedValue, params Parameter[] parameters)
59+
{
60+
var key = new object();
61+
62+
_builder.RegisterType<TImplementation>()
63+
.Keyed<TService>(key)
64+
.As<TService>()
65+
.WithParameters(parameters)
66+
.InstancePerLifetimeScope();
67+
68+
providedValue = CreateProvidedValue<TService>(c => c.ResolveKeyed<TService>(key));
69+
70+
return this;
71+
}
72+
73+
/// <summary>
74+
/// Register the specified object to the container as the specified service type and resolve it.
75+
/// </summary>
76+
/// <typeparam name="TService">The type to register the object as</typeparam>
77+
/// <param name="instance">The object to register into the container</param>
78+
/// <returns>The current <see cref="AutoSubstituteBuilder"/>.</returns>
79+
public AutoSubstituteBuilder Provide<TService>(TService instance)
80+
where TService : class
81+
{
82+
_builder.RegisterInstance(instance);
83+
84+
return this;
85+
}
86+
87+
/// <summary>
88+
/// Register the specified object to the container as the specified keyed service type and resolve it.
89+
/// </summary>
90+
/// <typeparam name="TService">The type to register the object as</typeparam>
91+
/// <param name="instance">The object to register into the container</param>
92+
/// <param name="serviceKey">The key to register the service with</param>
93+
/// <returns>The current <see cref="AutoSubstituteBuilder"/>.</returns>
94+
public AutoSubstituteBuilder Provide<TService>(TService instance, object serviceKey)
95+
where TService : class
96+
{
97+
_builder.Register(_ => instance)
98+
.Keyed<TService>(serviceKey)
99+
.InstancePerLifetimeScope();
100+
101+
return this;
102+
}
103+
104+
/// <summary>
105+
/// Registers to the container and returns a substitute for a given concrete class given the explicit constructor parameters.
106+
/// This is used for concrete classes where NSubstitutes won't be created by default by the container when using Resolve.
107+
/// For advanced uses consider using directly <see cref="Substitute.For{TService}"/> and then calling <see cref="Provide{TService}(TService)"/> so that type is used on dependencies for other Resolved types.
108+
/// </summary>
109+
/// <typeparam name="TService">The type to register and return a substitute for</typeparam>
110+
/// <param name="parameters">Optional constructor parameters</param>
111+
/// <returns>An instance to help configure the substitution.</returns>
112+
public SubstituteForBuilder<TService> SubstituteFor<TService>(params object[] parameters)
113+
where TService : class
114+
{
115+
var substitute = Substitute.For<TService>(parameters);
116+
117+
Provide(substitute);
118+
119+
return new SubstituteForBuilder<TService>(this, substitute);
120+
}
121+
122+
/// <summary>
123+
/// Registers to the container and returns a substitute for a given concrete class using autofac to resolve the constructor parameters.
124+
/// This is used for concrete classes where NSubstitutes won't be created by default by the container when using Resolve.
125+
/// For advanced uses consider using directly <see cref="Substitute.For{TService}"/> and then calling <see cref="Provide{TService}(TService)"/> so that type is used on dependencies for other Resolved types.
126+
/// </summary>
127+
/// <typeparam name="TService">The type to register and return a substitute for</typeparam>
128+
/// <param name="parameters">Any constructor parameters that Autofac can't resolve automatically</param>
129+
/// <returns>The current <see cref="AutoSubstituteBuilder"/>.</returns>
130+
public AutoSubstituteBuilder ResolveAndSubstituteFor<TService>(params Parameter[] parameters) where TService : class
131+
{
132+
_builder.RegisterType<TService>()
133+
.WithParameters(parameters)
134+
.InstancePerLifetimeScope();
135+
136+
return this;
137+
}
138+
139+
private IProvidedValue<TService> CreateProvidedValue<TService>(Func<IContainer, TService> factory)
140+
{
141+
var value = new ProvidedValue<TService>(factory);
142+
143+
_providedValues.Add(value);
144+
145+
return value;
146+
}
147+
148+
private interface IProvidedValue
149+
{
150+
void SetContainer(IContainer container);
151+
}
152+
153+
private class ProvidedValue<T> : IProvidedValue<T>, IProvidedValue
154+
{
155+
private readonly Func<IContainer, T> _factory;
156+
157+
private IContainer _container;
158+
159+
public ProvidedValue(Func<IContainer, T> factory)
160+
{
161+
_factory = factory;
162+
}
163+
164+
public T Value
165+
{
166+
get
167+
{
168+
if (_container is null)
169+
{
170+
throw new InvalidOperationException("Build must be called before using a provided value.");
171+
}
172+
173+
return _factory(_container);
174+
}
175+
}
176+
177+
public void SetContainer(IContainer container) => _container = container;
178+
}
179+
}
180+
}

‎AutofacContrib.NSubstitute/AutofacContrib.NSubstitute.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Autofac" Version="4.9.4" />
14-
<PackageReference Include="NSubstitute" Version="4.2.1" />
13+
<PackageReference Include="Autofac" Version="5.2.0" />
14+
<PackageReference Include="NSubstitute" Version="4.2.2" />
1515
</ItemGroup>
1616

1717
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace AutofacContrib.NSubstitute
2+
{
3+
public interface IProvidedValue<T>
4+
{
5+
T Value { get; }
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
3+
namespace AutofacContrib.NSubstitute
4+
{
5+
/// <summary>
6+
/// A class to configure substituted services.
7+
/// </summary>
8+
/// <typeparam name="TService">The type of the substituted service.</typeparam>
9+
public class SubstituteForBuilder<TService>
10+
where TService : class
11+
{
12+
private readonly AutoSubstituteBuilder _builder;
13+
private readonly TService _service;
14+
15+
internal SubstituteForBuilder(AutoSubstituteBuilder builder, TService service)
16+
{
17+
_builder = builder;
18+
_service = service;
19+
}
20+
21+
/// <summary>
22+
/// Allows for configuration of the service.
23+
/// </summary>
24+
/// <param name="action">The delegate to configure the service.</param>
25+
/// <returns>The original <see cref="AutoSubstituteBuilder"/>.</returns>
26+
public AutoSubstituteBuilder Configure(Action<TService> action)
27+
{
28+
action(_service);
29+
return _builder;
30+
}
31+
}
32+
33+
}

‎BREAKING_CHANGES.md

+11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
AutofacContrib.NSubstitute (AutoSubstitute) Breaking Changes
22
============================================================
33

4+
Version 6.0.0
5+
-------------
6+
7+
Removed `AutoSubstitute.Provide(...)` methods and added `AutoSubstitute.Configure()` with a builder pattern.
8+
9+
### Reason
10+
Autofac now enforces immutability and containers cannot be changed after being built.
11+
12+
### Workaround
13+
Update usage of `.Provide(...)` to use the builder pattern instead.
14+
415
Version 4.0.0
516
-------------
617

‎GitVersionConfig.yaml

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

‎README.md

+12-10
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,10 @@ And then the following tests:
117117
public void Example_test_with_concrete_type_provided()
118118
{
119119
const int val = 3;
120-
var autoSubstitute = new AutoSubstitute();
120+
using var autoSubstitute = AutoSubstitute.Configure()
121+
.Provide<IDependency2, Dependency2>(out _)
122+
.Build();
121123
autoSubstitute.Resolve<IDependency2>().SomeOtherMethod().Returns(val); // This shouldn't do anything because of the next line
122-
autoSubstitute.Provide<IDependency2, Dependency2>();
123124
autoSubstitute.Resolve<IDependency1>().SomeMethod(Arg.Any<int>()).Returns(c => c.Arg<int>());
124125

125126
var result = autoSubstitute.Resolve<MyClass>().AMethod();
@@ -132,9 +133,10 @@ public void Example_test_with_concrete_object_provide()
132133
{
133134
const int val1 = 3;
134135
const int val2 = 2;
135-
var autoSubstitute = new AutoSubstitute();
136+
using var autoSubstitute = AutoSubstitute.Configure()
137+
.Provide(new ConcreteClass(val2))
138+
.Build();
136139
autoSubstitute.Resolve<IDependency2>().SomeOtherMethod().Returns(val1);
137-
autoSubstitute.Provide(new ConcreteClass(val2));
138140

139141
var result = autoSubstitute.Resolve<MyClassWithConcreteDependency>().AMethod();
140142

@@ -151,9 +153,10 @@ public void Example_test_with_substitute_for_concrete()
151153
const int val1 = 3;
152154
const int val2 = 2;
153155
const int val3 = 10;
154-
var autoSubstitute = new AutoSubstitute();
156+
using var autoSubstitute = AutoSubstitute.Configure()
157+
.SubstituteFor<ConcreteClass>(val2).Configure(c => c.Add(Arg.Any<int>()).Returns(val3))
158+
.Build();
155159
autoSubstitute.Resolve<IDependency2>().SomeOtherMethod().Returns(val1);
156-
autoSubstitute.SubstituteFor<ConcreteClass>(val2).Add(Arg.Any<int>()).Returns(val3);
157160

158161
var result = autoSubstitute.Resolve<MyClassWithConcreteDependency>().AMethod();
159162

@@ -206,10 +209,9 @@ public void Example_test_with_substitute_for_concrete_resolved_from_autofac()
206209
const int val1 = 2;
207210
const int val2 = 3;
208211
const int val3 = 4;
209-
var AutoSubstitute = new AutoSubstitute();
210-
// Much better / more maintainable than:
211-
//AutoSubstitute.SubstituteFor<ConcreteClassWithDependency>(AutoSubstitute.Resolve<IDependency1>(), val1);
212-
AutoSubstitute.ResolveAndSubstituteFor<ConcreteClassWithDependency>(new TypedParameter(typeof(int), val1));
212+
using var AutoSubstitute = AutoSubstitute.Configure()
213+
.ResolveAndSubstituteFor<ConcreteClassWithDependency>(new TypedParameter(typeof(int), val1))
214+
.Build();
213215
AutoSubstitute.Resolve<IDependency2>().SomeOtherMethod().Returns(val2);
214216
AutoSubstitute.Resolve<IDependency1>().SomeMethod(val1).Returns(val3);
215217

‎global.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"sdk": {
3+
"version": "3.1.302",
4+
"rollForward": "feature"
5+
}
6+
}

0 commit comments

Comments
 (0)
Please sign in to comment.