Skip to content

Commit e7b2b78

Browse files
authored
Merge pull request #2 from lucasteles/provide-structs
Allow providing Value Types and non generic overloads
2 parents 5b87fa0 + 64e7b1d commit e7b2b78

File tree

9 files changed

+288
-21
lines changed

9 files changed

+288
-21
lines changed

.github/workflows/build.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,19 @@ jobs:
2424
- name: Checkout code
2525
uses: actions/checkout@v2
2626

27+
- name: Setup .NET Core
28+
uses: actions/setup-dotnet@v2
29+
with:
30+
global-json-file: global.json
31+
2732
- name: Build
2833
run: dotnet build ./src/FakeItEasy.AutoFakeIt/FakeItEasy.AutoFakeIt.csproj --configuration Release
2934

3035
- name: Test
3136
run: dotnet test ./tests/FakeItEasy.AutoFakeIt.UnitTests/FakeItEasy.AutoFakeIt.UnitTests.csproj /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput='./../../coverage/'
3237

3338
- name: Upload coverage to Codecov
34-
uses: codecov/codecov-action@v1.2.1
39+
uses: codecov/codecov-action@v3
3540
with:
3641
token: ${{ secrets.CODECOV_TOKEN }}
3742
directory: ./coverage/

.github/workflows/publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ jobs:
1313
- uses: actions/checkout@v2
1414

1515
- name: Setup .NET Core
16-
uses: actions/setup-dotnet@v1
16+
uses: actions/setup-dotnet@v2
1717
with:
18-
dotnet-version: 5.0.301
18+
global-json-file: global.json
1919

2020
- name: publish FakeItEasy.AutoFakeIt
2121
id: publish_nuget_common

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
3-
"version": "5.0.301",
3+
"version": "6.0.100",
44
"rollForward": "latestFeature"
55
}
66
}

src/FakeItEasy.AutoFakeIt/AutoFakeIt.cs

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,18 @@ public class AutoFakeIt
2020
/// <returns>A class with all its dependencies faked.</returns>
2121
/// <exception cref="ArgumentException">Throws an ArgumentException if we can't find a public constructor
2222
/// with "fakeable" dependencies.</exception>
23-
public T Generate<T>() where T : class
23+
public T Generate<T>() where T : notnull => (T)Generate(typeof(T));
24+
25+
/// <summary>
26+
/// Generates an object of the specified type automatically injecting FakeItEasy's fakes for all its dependencies.
27+
/// </summary>
28+
/// <param name="type">The type of the class you want to generate, usually your System Under Test.</param>
29+
/// <returns>A class with all its dependencies faked.</returns>
30+
/// <exception cref="ArgumentException">Throws an ArgumentException if we can't find a public constructor
31+
/// with "fakeable" dependencies.</exception>
32+
public object Generate(Type type)
2433
{
25-
var constructors = typeof(T).GetConstructors()
34+
var constructors = type.GetConstructors()
2635
.OrderByDescending(ctor => ctor.GetParameters().Length)
2736
.ToList();
2837

@@ -33,7 +42,7 @@ public T Generate<T>() where T : class
3342
{
3443
var candidateFakeObjects = GenerateCandidateFakeObjects(ctor);
3544

36-
var generatedObject = (T)ctor.Invoke(candidateFakeObjects.Values.ToArray());
45+
var generatedObject = ctor.Invoke(candidateFakeObjects.Values.ToArray());
3746

3847
InsertMissingFakedObjects(candidateFakeObjects);
3948

@@ -46,9 +55,10 @@ public T Generate<T>() where T : class
4655
}
4756
}
4857

49-
throw new ArgumentException($"No suitable constructor found for type '{typeof(T)}'.", lastThrownException);
58+
throw new ArgumentException($"No suitable constructor found for type '{type}'.", lastThrownException);
5059
}
5160

61+
5262
private Dictionary<Type, object> GenerateCandidateFakeObjects(ConstructorInfo ctor)
5363
{
5464
var candidateFakeObjects = new Dictionary<Type, object>();
@@ -77,17 +87,25 @@ private void InsertMissingFakedObjects(Dictionary<Type, object> candidateFakeObj
7787
/// </summary>
7888
/// <typeparam name="T">The fake object you want to retrieve.</typeparam>
7989
/// <returns>An object of the given type, either previously provided or a new one just generated with FakeItEasy.</returns>
80-
public T Resolve<T>() where T : class
90+
public T Resolve<T>() where T : notnull => (T)Resolve(typeof(T));
91+
92+
/// <summary>
93+
/// Returns the object used for the type passed. If an object of the given type was not explicitly
94+
/// provided, it will create a FakeItEasy's fake that will be used for all subsequent calls.
95+
/// </summary>
96+
/// <returns>An object of the given type, either previously provided or a new one just generated with FakeItEasy.</returns>
97+
public object Resolve(Type type)
8198
{
82-
if (_fakedObjects.ContainsKey(typeof(T)))
83-
return (T)_fakedObjects[typeof(T)];
99+
if (_fakedObjects.ContainsKey(type))
100+
return _fakedObjects[type];
84101
else
85102
{
86-
_fakedObjects[typeof(T)] = A.Fake<T>();
87-
return (T)_fakedObjects[typeof(T)];
103+
_fakedObjects[type] = Create.Fake(type);
104+
return _fakedObjects[type];
88105
}
89106
}
90107

108+
91109
/// <summary>
92110
/// Explicitly sets an object to use when someone asks for an object of the given type, either on a
93111
/// <see cref="Resolve{T}"/> or for a dependency on <see cref="Generate{T}"/>. </summary>
@@ -97,12 +115,27 @@ public T Resolve<T>() where T : class
97115
/// <param name="dependency">The object you want to set.</param>
98116
/// <typeparam name="T">The type of the object you want to register it as. You can set an object for a specific
99117
/// type, for example, you can provide an object to be used when someone asks for an interface.</typeparam>
100-
public void Provide<T>(T dependency) where T : class
118+
public void Provide<T>(T dependency) where T : notnull => Provide(typeof(T), dependency);
119+
120+
/// <summary>
121+
/// Explicitly sets an object to use when someone asks for an object of the given type, either on a
122+
/// <see cref="Resolve"/> or for a dependency on <see cref="Generate"/>. </summary>
123+
/// <remarks><para>This is useful for providing your own specific instance of a type, either because you can't
124+
/// use an automatically generated fake, or because you have a concrete type that you prefer to use.</para>
125+
/// <param name="registerType">The type of the object you want to provide.</param>
126+
/// <param name="dependency">The object you want to set.</param>
127+
/// <para>It will override any previously registered object of the same type of <param name="registerType"/>.</para></remarks>
128+
/// <exception cref="ArgumentException">Throws an ArgumentException if dependency is not assignable to registerType.</exception>
129+
public void Provide(Type registerType, object dependency)
101130
{
102-
if (_fakedObjects.ContainsKey(typeof(T)))
103-
_fakedObjects[typeof(T)] = dependency;
131+
if (!registerType.IsInstanceOfType(dependency))
132+
throw new ArgumentException(
133+
$"Dependency type '{registerType}' is not assignable to '{dependency.GetType()}'.");
134+
135+
if (_fakedObjects.ContainsKey(registerType))
136+
_fakedObjects[registerType] = dependency;
104137
else
105-
_fakedObjects.Add(typeof(T), dependency);
138+
_fakedObjects.Add(registerType, dependency);
106139
}
107140
}
108141
}

src/FakeItEasy.AutoFakeIt/FakeItEasy.AutoFakeIt.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<RepositoryType>Git</RepositoryType>
1919
<PackageTags>fakeiteasy, unit-testing, nunit, xunit, mstest</PackageTags>
2020
<PackageLicenseExpression>MIT</PackageLicenseExpression>
21-
<Company/>
21+
<Company />
2222
<PublishRepositoryUrl>true</PublishRepositoryUrl>
2323
<EmbedUntrackedSources>true</EmbedUntrackedSources>
2424
<AllowedOutputExtensionsInPackageBuildOutputFolder>.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
@@ -32,7 +32,7 @@
3232
<DebugSymbols>true</DebugSymbols>
3333
</PropertyGroup>
3434
<ItemGroup>
35-
<PackageReference Include="FakeItEasy" Version="4.0.0"/>
36-
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
35+
<PackageReference Include="FakeItEasy" Version="4.0.0" />
36+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
3737
</ItemGroup>
3838
</Project>

tests/FakeItEasy.AutoFakeIt.UnitTests/FakeItEasy.AutoFakeIt.UnitTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp3.1</TargetFramework>
4+
<TargetFramework>net6.0</TargetFramework>
55

66
<IsPackable>false</IsPackable>
77

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
using FakeItEasy.AutoFakeIt.UnitTests.Stubs;
2+
using FakeItEasy.Core;
3+
using FluentAssertions;
4+
using NUnit.Framework;
5+
using System;
6+
7+
namespace FakeItEasy.AutoFakeIt.UnitTests.Specs
8+
{
9+
public class AutoFakeItTestsNonGeneric
10+
{
11+
[Test]
12+
public void GenerateShouldReturnAnInstanceOfClassWithoutDependencies()
13+
{
14+
var sut = new AutoFakeIt().Generate(typeof(SimpleSut));
15+
16+
sut.Should().NotBeNull();
17+
}
18+
19+
[Test]
20+
public void GenerateShouldThrowExceptionWhenClassHasNoConstructorsWithFakeableParameters()
21+
{
22+
Action act = () => new AutoFakeIt().Generate(typeof(UnfakeableSut));
23+
24+
act.Should().Throw<ArgumentException>()
25+
.WithMessage($"No suitable constructor found for type '{typeof(UnfakeableSut).FullName}'.")
26+
.WithInnerException<FakeCreationException>();
27+
}
28+
29+
[Test]
30+
public void GenerateShouldReturnAnInstanceOfClassWithFakedDependencies()
31+
{
32+
var sut = (DependenciesSut)new AutoFakeIt().Generate(typeof(DependenciesSut));
33+
34+
sut.Should().NotBeNull();
35+
A.CallTo(() => sut.DependencyA.Method()).Returns("Faked");
36+
sut.DependencyA.Method().Should().Be("Faked");
37+
}
38+
39+
[Test]
40+
public void GenerateShouldReturnAnInstanceOfClassWithSubdependencies()
41+
{
42+
var autoFakeIt = new AutoFakeIt();
43+
var sut = (SubdependenciesSut)autoFakeIt.Generate(typeof(SubdependenciesSut));
44+
45+
sut.Should().NotBeNull();
46+
A.CallTo(() => sut.Dependency.Subdependency.Method()).Returns("Faked");
47+
sut.Dependency.Subdependency.Method().Should().Be("Faked");
48+
}
49+
50+
[Test]
51+
public void GenerateShouldReturnAnInstanceOfClassWithSubdependenciesDespiteItsOrder()
52+
{
53+
var autoFakeIt = new AutoFakeIt();
54+
autoFakeIt.Resolve(typeof(Dependency));
55+
var sut = autoFakeIt.Generate(typeof(SubdependenciesSut));
56+
57+
sut.Should().NotBeNull();
58+
}
59+
60+
[Test]
61+
public void GenerateShouldReturnAnInstanceOfClassWithTheMostFakedDependenciesPossible()
62+
{
63+
var sut = (MultipleConstructorsSut)new AutoFakeIt().Generate(typeof(MultipleConstructorsSut));
64+
65+
sut.Should().NotBeNull();
66+
A.CallTo(() => sut.DependencyB.Method()).Returns("Faked");
67+
sut.DependencyB.Method().Should().Be("Faked");
68+
}
69+
70+
[Test]
71+
public void GenerateShouldReusePreviouslyResolvedType()
72+
{
73+
var autoFakeIt = new AutoFakeIt();
74+
var depA = autoFakeIt.Resolve(typeof(DependencyA));
75+
var sut = (DependenciesSut)autoFakeIt.Generate(typeof(DependenciesSut));
76+
77+
sut.DependencyA.Should().Be(depA);
78+
}
79+
80+
[Test]
81+
public void ResolveShouldReturnGeneratedFakeTypes()
82+
{
83+
var autoFakeIt = new AutoFakeIt();
84+
var sut = (DependenciesSut)autoFakeIt.Generate(typeof(DependenciesSut));
85+
86+
autoFakeIt.Resolve(typeof(DependencyA)).Should().Be(sut.DependencyA);
87+
}
88+
89+
[Test]
90+
public void ResolveShouldGenerateAndReturnAFakeTypeWhenNoneIsAvailable()
91+
{
92+
var autoFakeIt = new AutoFakeIt();
93+
var depA = (DependencyA)autoFakeIt.Resolve(typeof(DependencyA));
94+
95+
depA.Should().NotBeNull();
96+
A.CallTo(() => depA.Method()).Returns("Faked");
97+
depA.Method().Should().Be("Faked");
98+
}
99+
100+
[Test]
101+
public void ResolveShouldReturnProvidedDependency()
102+
{
103+
var autoFakeIt = new AutoFakeIt();
104+
var depA = new DependencyA();
105+
autoFakeIt.Provide(depA.GetType(), depA);
106+
107+
autoFakeIt.Resolve(typeof(DependencyA)).Should().Be(depA);
108+
}
109+
110+
[Test]
111+
public void ResolveShouldReturnProvidedValueTypeDependency()
112+
{
113+
var autoFakeIt = new AutoFakeIt();
114+
var structDep = new ValueDependency();
115+
autoFakeIt.Provide(structDep.GetType(), structDep);
116+
117+
autoFakeIt.Resolve(typeof(ValueDependency)).Should().Be(structDep);
118+
}
119+
120+
[Test]
121+
public void GenerateShouldReturnProvidedValueTypeDependency()
122+
{
123+
var autoFakeIt = new AutoFakeIt();
124+
var structDep = new ValueDependency();
125+
autoFakeIt.Provide(structDep.GetType(), structDep);
126+
127+
var generated = (StructDependenciesSut)autoFakeIt.Generate(typeof(StructDependenciesSut));
128+
generated.ValueDependency.Should().Be(structDep);
129+
}
130+
131+
[Test]
132+
public void ResolveShouldUpdateProvidedDependencyWhenOneIsAlreadyAvailable()
133+
{
134+
var autoFakeIt = new AutoFakeIt();
135+
var depA = new DependencyA();
136+
autoFakeIt.Provide(depA.GetType(), depA);
137+
var depA2 = new DependencyA();
138+
autoFakeIt.Provide(depA.GetType(), depA2);
139+
140+
autoFakeIt.Resolve(typeof(DependencyA)).Should().Be(depA2);
141+
}
142+
143+
[Test]
144+
public void ResolveShouldReturnObjectsRegisteredWithExplicitTypes()
145+
{
146+
var autoFakeIt = new AutoFakeIt();
147+
var depA = new DependencyA();
148+
autoFakeIt.Provide(typeof(IDependencyA), depA);
149+
150+
autoFakeIt.Resolve(typeof(IDependencyA)).Should().Be(depA);
151+
}
152+
153+
[Test]
154+
public void GenerateShouldUseProvidedDependency()
155+
{
156+
var autoFakeIt = new AutoFakeIt();
157+
var depA = new DependencyA();
158+
autoFakeIt.Provide(typeof(DependencyA), depA);
159+
160+
var sut = (DependenciesSut)autoFakeIt.Generate(typeof(DependenciesSut));
161+
sut.DependencyA.Should().Be(depA);
162+
}
163+
164+
[Test]
165+
public void ProvideShouldThrowExceptionWhenDependencyIsNotAssignableToRegisterType()
166+
{
167+
var autoFakeIt = new AutoFakeIt();
168+
var depA = new DependencyA();
169+
var act = () => autoFakeIt.Provide(typeof(DependencyB), depA);
170+
171+
act.Should().Throw<ArgumentException>()
172+
.WithMessage($"Dependency type '{typeof(DependencyB).FullName}' is not assignable to '{typeof(DependencyA).FullName}'.");
173+
}
174+
}
175+
}

tests/FakeItEasy.AutoFakeIt.UnitTests/Specs/AutoFakeItTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,26 @@ public void ResolveShouldReturnProvidedDependency()
107107
autoFakeIt.Resolve<DependencyA>().Should().Be(depA);
108108
}
109109

110+
[Test]
111+
public void ResolveShouldReturnProvidedValueTypeDependency()
112+
{
113+
var autoFakeIt = new AutoFakeIt();
114+
var structDep = new ValueDependency();
115+
autoFakeIt.Provide(structDep);
116+
117+
autoFakeIt.Resolve<ValueDependency>().Should().Be(structDep);
118+
}
119+
120+
[Test]
121+
public void GenerateShouldReturnProvidedValueTypeDependency()
122+
{
123+
var autoFakeIt = new AutoFakeIt();
124+
var structDep = new ValueDependency();
125+
autoFakeIt.Provide(structDep);
126+
127+
autoFakeIt.Generate<StructDependenciesSut>().ValueDependency.Should().Be(structDep);
128+
}
129+
110130
[Test]
111131
public void ResolveShouldUpdateProvidedDependencyWhenOneIsAlreadyAvailable()
112132
{
@@ -139,5 +159,16 @@ public void GenerateShouldUseProvidedDependency()
139159
var sut = autoFakeIt.Generate<DependenciesSut>();
140160
sut.DependencyA.Should().Be(depA);
141161
}
162+
163+
[Test]
164+
public void GenericResolveShouldWorkWithNonGenericProvide()
165+
{
166+
var autoFakeIt = new AutoFakeIt();
167+
var depA = new DependencyA();
168+
autoFakeIt.Provide(typeof(DependencyA), depA);
169+
170+
var resolvedDepA = autoFakeIt.Resolve<DependencyA>();
171+
resolvedDepA.Should().NotBeNull();
172+
}
142173
}
143174
}

0 commit comments

Comments
 (0)