Skip to content
This repository was archived by the owner on Jun 30, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
973fd6d
Initial support for a legacy API shim
kzu Jul 23, 2019
b83445a
Add codegen support for new Mock<T>
kzu Jul 23, 2019
a58ad2d
Setup should happen as soon as possible
kzu Jul 23, 2019
72a1a28
Make sure we use the test calling assembly
adalon Jul 24, 2019
df46b70
Added SkipBehavior support for behavior pipeline
adalon Jul 24, 2019
5aaca90
Add new behavior for setup scope runs
kzu Jul 24, 2019
0c2ff79
Add setup overloads to avoid the mock T argument
kzu Jul 24, 2019
60237fe
Added AsMoq extension method
adalon Jul 24, 2019
eda348b
Simplify Behavior implementation by using SkipBehaviors
kzu Jul 24, 2019
edc7b68
Revert "Add setup overloads to avoid the mock T argument"
kzu Jul 24, 2019
efeefc3
Fix failing tests
kzu Jul 24, 2019
4e9d640
Move setup scope to Moq
kzu Jul 24, 2019
004e96e
Fix visiblity of various Sdk-like classes in the Moq main assembly
kzu Jul 24, 2019
91b4853
Added CallBase support
adalon Jul 24, 2019
58a67fe
Don't run FixupImports twice
kzu Jul 25, 2019
3f191e7
Ensure both analyzers and codefixers have NuGetPackageId metadata
kzu Jul 25, 2019
4c5db10
Optimize codegen performance for real world solutions
kzu Jul 25, 2019
e159b48
Do not clean unused namespaces, since it is costly for little benefit
kzu Jul 25, 2019
814afdc
Bump to latest Roslyn for VS2017 and updated supported code fix names
kzu Jul 25, 2019
708db5b
Ensure a clean restore is performed always, add CI feed
kzu Jul 25, 2019
20b9fd2
Set proper names for CallBase tests
adalon Jul 25, 2019
6143a8a
Bump TFV to the 16.0+ official one supporting NS2
kzu Jul 29, 2019
e5b1c4e
Properly generate code for generic mocks
kzu Aug 3, 2019
d9bbc2e
Add support for mocking generic types
kzu Aug 6, 2019
34a6c49
Move OverrideAllMembersCodeFix to CodeFix assembly to avoid csc error
kzu Aug 6, 2019
1e97239
Don't assume mocked types will be public
kzu Aug 6, 2019
e30d526
Cleanup and encapsulate the batch code fixer and avoid state capturing
kzu Aug 6, 2019
f4b4be4
Delete OverrideAllMembersCodeFix class that moved to CodeFix
kzu Aug 6, 2019
d5e5fd7
Re-enable end to end tests for VB since they work now
kzu Aug 6, 2019
cfddaf2
Fix roslyn internals tests from moved RoslynInternals.cs file
kzu Aug 8, 2019
2e4f6e8
Fix minor style issues flagged by codefactor.io
kzu Aug 8, 2019
462cf05
Unify naming conventions for runtime lookup
kzu Aug 8, 2019
83a22b0
Minor docs tweaks to CallBase
kzu Aug 8, 2019
db87731
Drastically simplify As<T> support by adding new Mock<T...Tn>
kzu Aug 8, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion NuGet.Config
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nugetizer" value="https://ci.appveyor.com/nuget/nugetizer3000" />
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="nugetizer" value="https://ci.appveyor.com/nuget/nugetizer3000" />
<add key="kzu" value="https://kzu.blob.core.windows.net/nuget/index.json" />
</packageSources>
</configuration>
8 changes: 4 additions & 4 deletions src/Moq/Moq.CodeAnalysis/MockNamingConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ namespace Moq
public class MockNamingConvention : NamingConvention
{
/// <summary>
/// Gets the generated code namespace, which is <see cref="MockNaming.Namespace"/>.
/// Gets the generated code namespace, which is <see cref="MockNaming.DefaultNamespace"/>.
/// </summary>
public override string Namespace => MockNaming.Namespace;
public override string Namespace => MockNaming.DefaultNamespace;

/// <summary>
/// Gets the generated type names suffix, which is <see cref="MockNaming.NameSuffix"/>.
/// Gets the generated type names suffix, which is <see cref="MockNaming.DefaultSuffix"/>.
/// </summary>
public override string NameSuffix => MockNaming.NameSuffix;
public override string NameSuffix => MockNaming.DefaultSuffix;
}
}
4 changes: 2 additions & 2 deletions src/Moq/Moq.CodeAnalysis/RecursiveMockAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ static void ReportDiagnostics(SyntaxNodeAnalysisContext context, INamedTypeSymbo
new Dictionary<string, string>
{
{ "TargetFullName", name },
{ "Symbols", type.ToFullMetadataName() },
{ "Symbols", type.ToFullName() },
{ "RecursiveSymbols", "" },
}.ToImmutableDictionary(),
name));
Expand All @@ -136,7 +136,7 @@ static void ReportDiagnostics(SyntaxNodeAnalysisContext context, INamedTypeSymbo
new Dictionary<string, string>
{
{ "TargetFullName", name },
{ "Symbols", type.ToFullMetadataName() },
{ "Symbols", type.ToFullName() },
{ "RecursiveSymbols", "" },
}.ToImmutableDictionary(),
name));
Expand Down
2 changes: 1 addition & 1 deletion src/Moq/Moq.CodeFix/MockGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public MockGenerator(NamingConvention naming)
{
new DefaultImports(typeof(LazyInitializer).Namespace, typeof(IMocked).Namespace),
}
.Concat(GetDefaultProcessors())
.Concat(GetDefaultProcessors().Where(p => !(p is FixupImports)))
.Concat(new IDocumentProcessor[]
{
new CSharpMocked(),
Expand Down
4 changes: 2 additions & 2 deletions src/Moq/Moq.CodeFix/Moq.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

<!-- CodeFix analyzers should only be added to DTBs since they depend on
editor features that are not available from csc/vbc -->
<CodeFix Include="$(MSBuildThisFileDirectory)netstandard.dll" />
<CodeFix Include="$(MSBuildThisFileDirectory)Moq.CodeFix.dll" />
<CodeFix Include="$(MSBuildThisFileDirectory)netstandard.dll" NuGetPackageId="Moq" />
<CodeFix Include="$(MSBuildThisFileDirectory)Moq.CodeFix.dll" NuGetPackageId="Moq" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 3 additions & 1 deletion src/Moq/Moq.Sdk.Tests/Fakes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class FakeSetup : IMockSetup
public class FakeInvocation : IMethodInvocation
{
public FakeInvocation() => Target = new Mocked();

public IArgumentCollection Arguments { get; set; }

public IDictionary<string, object> Context { get; set; }
Expand All @@ -51,6 +51,8 @@ public class FakeInvocation : IMethodInvocation

public object Target { get; set; }

public HashSet<Type> SkipBehaviors { get; } = new HashSet<Type>();

public IMethodReturn CreateExceptionReturn(Exception exception) => new FakeReturn { Exception = exception };

public IMethodReturn CreateValueReturn(object returnValue, params object[] allArguments) => new FakeReturn { ReturnValue = returnValue };
Expand Down
3 changes: 2 additions & 1 deletion src/Moq/Moq.Sdk.Tests/Moq.Sdk.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Sdk Name="Microsoft.Build.CentralPackageVersions" />

<PropertyGroup>
<TargetFramework>net471</TargetFramework>
<TargetFramework>net472</TargetFramework>
<IncludeXunit>true</IncludeXunit>
<IncludeMSBuild>true</IncludeMSBuild>

Expand All @@ -16,6 +16,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Stunts\Stunts.CodeFix\Stunts.CodeFix.csproj" />
<ProjectReference Include="..\Moq.Sdk\Moq.Sdk.csproj" />
<ProjectReference Include="..\..\Stunts\Stunts\Stunts.csproj" />
<ProjectReference Include="..\..\Stunts\Stunts.Sdk\Stunts.Sdk.csproj" />
Expand Down
14 changes: 0 additions & 14 deletions src/Moq/Moq.Sdk.Tests/StrictMockBehaviorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,5 @@ public void AppliesToAllInvocations()
public void ThrowsStrictMockException()
=> Assert.Throws<StrictMockException>(() =>
new StrictMockBehavior().Execute(new FakeInvocation(), () => throw new NotImplementedException()));

[Fact]
public void ThrowsIfNullInvocation()
=> Assert.Throws<ArgumentNullException>(() =>
new StrictMockBehavior().Execute(null, () => throw new NotImplementedException()));

[Fact]
public void DoesNotThrowIfSetupScopeActive()
{
using (new SetupScope())
{
new StrictMockBehavior().Execute(new FakeInvocation(), () => (m, n) => m.CreateValueReturn(null));
}
}
}
}
2 changes: 1 addition & 1 deletion src/Moq/Moq.Sdk/IMock`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// <summary>
/// Provides introspection information about a mock.
/// </summary>
public interface IMock<out T> : IMock, IFluentInterface
public interface IMock<out T> : IMock, IFluentInterface where T : class
{
/// <summary>
/// The mock object this introspection data belongs to.
Expand Down
8 changes: 2 additions & 6 deletions src/Moq/Moq.Sdk/MockDecorator`1.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Stunts;

namespace Moq.Sdk
namespace Moq.Sdk
{
/// <summary>
/// Decorator implementation over an <see cref="IMock"/>.
/// </summary>
public abstract class MockDecorator<T> : MockDecorator, IMock<T>
public abstract class MockDecorator<T> : MockDecorator, IMock<T> where T : class
{
readonly IMock<T> mock;

Expand Down
12 changes: 6 additions & 6 deletions src/Moq/Moq.Sdk/MockExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ public static class MockExtensions
/// <summary>
/// Gets the introspection information for a mocked object instance.
/// </summary>
public static IMock<T> AsMock<T>(this T instance)
public static IMock<T> AsMock<T>(this T instance) where T : class
=> (instance as IMocked)?.Mock.As(instance) ?? throw new ArgumentException(Strings.TargetNotMock, nameof(instance));

/// <summary>
/// Clones a mock by creating a new instance of the <see cref="IMock.Object"/>
/// from <paramref name="mock"/> and copying its behaviors, invocations and state.
/// </summary>
public static IMock<T> Clone<T>(this IMock<T> mock)
public static IMock<T> Clone<T>(this IMock<T> mock) where T : class
{
if (!mock.State.TryGetValue<object[]>(".ctor", out var ctor))
throw new ArgumentException("No constructor state found for cloning.");
Expand All @@ -52,7 +52,7 @@ public static IMock<T> Clone<T>(this IMock<T> mock)
/// Gets the invocations performed on the mock so far that match the given
/// setup lambda.
/// </summary>
public static IEnumerable<IMethodInvocation> InvocationsFor<T>(this IMock<T> mock, Action<T> action)
public static IEnumerable<IMethodInvocation> InvocationsFor<T>(this IMock<T> mock, Action<T> action) where T : class
{
using (new SetupScope())
{
Expand All @@ -66,7 +66,7 @@ public static IEnumerable<IMethodInvocation> InvocationsFor<T>(this IMock<T> moc
/// Gets the invocations performed on the mock so far that match the given
/// setup lambda.
/// </summary>
public static IEnumerable<IMethodInvocation> InvocationsFor<T, TResult>(this IMock<T> mock, Func<T, TResult> function)
public static IEnumerable<IMethodInvocation> InvocationsFor<T, TResult>(this IMock<T> mock, Func<T, TResult> function) where T : class
{
using (new SetupScope())
{
Expand All @@ -76,9 +76,9 @@ public static IEnumerable<IMethodInvocation> InvocationsFor<T, TResult>(this IMo
}
}

static IMock<T> As<T>(this IMock mock, T target) => mock == null ? null : new Mock<T>(mock, target);
static IMock<T> As<T>(this IMock mock, T target) where T : class => mock == null ? null : new Mock<T>(mock, target);

class Mock<T> : IMock<T>
class Mock<T> : IMock<T> where T : class
{
IMock mock;

Expand Down
2 changes: 1 addition & 1 deletion src/Moq/Moq.Sdk/MockGeneratorAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Moq
/// Annotates a method that is a factory for mocks, so that a
/// compile-time or design-time generator can generate them ahead of time.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor)]
public class MockGeneratorAttribute : Attribute
{
}
Expand Down
44 changes: 30 additions & 14 deletions src/Moq/Moq.Sdk/MockNaming.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Stunts;

namespace Moq.Sdk
{
Expand All @@ -10,29 +9,46 @@ namespace Moq.Sdk
public static class MockNaming
{
/// <summary>
/// The namespace where generated mocks are declared.
/// The default namespace where generated mocks are declared.
/// </summary>
public const string Namespace = "Mocks";
public const string DefaultNamespace = "Mocks";

/// <summary>
/// The suffix added to mock type names.
/// The default suffix added to mock type names.
/// </summary>
public const string NameSuffix = "Mock";
public const string DefaultSuffix = "Mock";

/// <summary>
/// Gets the runtime mock name from its base type and implemented interfaces.
/// Gets the runtime mock name from its base type and optional additional
/// interfaces, using the <see cref="DefaultSuffix"/>.
/// </summary>
public static string GetName(Type baseType, Type[] implementedInterfaces)
{
Array.Sort(implementedInterfaces, Comparer<Type>.Create((x, y) => x.Name.CompareTo(y.Name)));
public static string GetName(Type baseType, Type[] additionalInterfaces)
=> GetName(DefaultSuffix, baseType, additionalInterfaces);

return baseType.Name + string.Join("", implementedInterfaces.Select(x => x.Name)) + NameSuffix;
}
/// <summary>
/// Gets the runtime mock name from its base type and optional additional interfaces
/// and the given <paramref name="suffix"/> appended to the type name.
/// </summary>
public static string GetName(string suffix, Type baseType, Type[] additionalInterfaces)
=> StuntNaming.GetName(suffix, baseType, additionalInterfaces);

/// <summary>
/// Gets the runtime mock full name from its base type and optional additional interfaces,
/// using the <see cref="DefaultNamespace"/> and <see cref="DefaultSuffix"/>.
/// </summary>
public static string GetFullName(Type baseType, params Type[] additionalInterfaces)
=> GetFullName(DefaultNamespace, DefaultSuffix, baseType, additionalInterfaces);

/// <summary>
/// Gets the runtime mock full name from its base type and implemented interfaces.
/// </summary>
public static string GetFullName(string @namespace, Type baseType, params Type[] additionalInterfaces)
=> GetFullName(@namespace, DefaultSuffix, baseType, additionalInterfaces);

/// <summary>
/// Gets the runtime mock full name from its base type and implemented interfaces.
/// </summary>
public static string GetFullName(Type baseType, Type[] implementedInterfaces)
=> Namespace + "." + GetName(baseType, implementedInterfaces);
public static string GetFullName(string @namespace, string suffix, Type baseType, params Type[] additionalInterfaces)
=> StuntNaming.GetFullName(@namespace, suffix, baseType, additionalInterfaces);
}
}
16 changes: 2 additions & 14 deletions src/Moq/Moq.Sdk/StrictMockBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ namespace Moq.Sdk
/// </summary>
public class StrictMockBehavior : IStuntBehavior
{
IStuntBehavior fallback = new DefaultValueBehavior();

/// <summary>
/// Always returns <see langword="true" />
/// </summary>
Expand All @@ -20,16 +18,6 @@ public class StrictMockBehavior : IStuntBehavior
/// <summary>
/// Throws <see cref="StrictMockException"/>.
/// </summary>
public IMethodReturn Execute(IMethodInvocation invocation, GetNextBehavior next)
{
if (invocation == null) throw new ArgumentNullException(nameof(invocation));

if (!SetupScope.IsActive)
throw new StrictMockException();

// Otherwise, fallback to returning default values so that
// the fluent setup API can do its work.
return fallback.Execute(invocation, next);
}
public IMethodReturn Execute(IMethodInvocation invocation, GetNextBehavior next) => throw new StrictMockException();
}
}
}
82 changes: 82 additions & 0 deletions src/Moq/Moq.Tests/CallBaseTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using Xunit;
using Moq.Sdk;
using Sample;

namespace Moq.Tests
{
public class CallBaseTests
{
[Fact]
public void CallBaseNotCalled()
{
var mock = Mock.Of<Calculator>();

mock.TurnOn();

Assert.False(mock.TurnOnCalled);
}

[Fact]
public void CallBaseCalledForMockConfig()
{
var mock = Mock.Of<Calculator>().CallBase();

mock.TurnOn();

Assert.True(mock.TurnOnCalled);
}

[Fact]
public void CallBaseCalledForInvocationConfig()
{
var mock = Mock.Of<Calculator>();

mock.Setup(x => x.TurnOn()).CallBase();

mock.TurnOn();

Assert.True(mock.TurnOnCalled);
}

[Fact]
public void ThrowsForStrictMockAndMissingSetup()
{
// Configure CallBase at the Mock level
var mock = Mock.Of<Calculator>(MockBehavior.Strict).CallBase();

Assert.Throws<StrictMockException>(() => mock.TurnOn());
}

[Fact]
public void CallBaseCalledForStrictMockAndMockConfig()
{
// Configure CallBase at the Mock level
var mock = Mock.Of<Calculator>(MockBehavior.Strict).CallBase();

mock.Setup(x => x.TurnOn());

mock.TurnOn();

Assert.True(mock.TurnOnCalled);

// And we make sure we throw for other missing setups
Assert.Throws<StrictMockException>(() => mock.Recall(""));
}

[Fact]
public void CallBaseCalledForStrickMockAndInvocationConfig()
{
var mock = Mock.Of<Calculator>(MockBehavior.Strict);

// Configure CallBase at the invocation level
mock.Setup(x => x.TurnOn()).CallBase();

mock.TurnOn();

Assert.True(mock.TurnOnCalled);

// And we make sure we throw for other missing setups
Assert.Throws<StrictMockException>(() => mock.Recall(""));
}
}
}
Loading