Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
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
4 changes: 1 addition & 3 deletions Generators/CombineGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.IO;
using System.Linq;
using System.Text;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;

Expand Down Expand Up @@ -35,6 +32,7 @@ private MemberDeclarationSyntax Combine(int count)
{
return MethodDeclaration(PredefinedType(Token(VoidKeyword)), "Combine")
.WithModifiers(TokenList(Token(TriviaList(Trivia(Documentation)), PublicKeyword, TriviaList())))
//.WithAttributeLists(SingletonList(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage"))))))
.WithTypeParameterList(TypeParameterList(SeparatedList(Enumerable.Range(0, count + 1).Select(type))))
.WithExpressionBody(ArrowExpressionClause(
InvocationExpression(IdentifierName("Combine"))
Expand Down
113 changes: 113 additions & 0 deletions Moq.AutoMock.Tests/ConstructorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;

/// <summary>
/// Based on https://gist.github.com/Keboo/d2f7f470d66fc4ff7d978ccb107b07cf
/// </summary>
namespace Moq.AutoMock.Tests
{
public interface IConstructorTest
{
void Run();
}

[ExcludeFromCodeCoverage]
public static class ConstructorTest
{
private class Test : IConstructorTest
{
public Dictionary<Type, object?> SpecifiedValues { get; }
= new Dictionary<Type, object?>();

private Type TargetType { get; }

public Test(Type targetType)
{
TargetType = targetType;
}

public Test(Test original)
{
TargetType = original.TargetType;
foreach (KeyValuePair<Type, object?> kvp in original.SpecifiedValues)
{
SpecifiedValues[kvp.Key] = kvp.Value;
}
}

public void Run()
{
foreach (ConstructorInfo constructor in TargetType.GetConstructors())
{
ParameterInfo[] parameters = constructor.GetParameters();

object?[] parameterValues = parameters
.Select(p => p.ParameterType)
.Select(t =>
{
if (SpecifiedValues.TryGetValue(t, out object? value))
{
return value;
}
Mock mock = (Mock)Activator.CreateInstance(typeof(Mock<>).MakeGenericType(t))!;
return mock.Object;
})
.ToArray();

for (int i = 0; i < parameters.Length; i++)
{
object?[] values = parameterValues.ToArray();
values[i] = null;

if (parameters[i].HasDefaultValue && parameters[i].DefaultValue is null)
{
//NB: no exception thrown
constructor.Invoke(values);
}
else
{
string parameterDisplay = $"'{parameters[i].Name}' ({parameters[i].ParameterType.Name})";
TargetInvocationException ex = Assert.ThrowsException<TargetInvocationException>(new Action(() =>
{
object? rv = constructor.Invoke(values);
throw new Exception($"Expected {nameof(ArgumentNullException)} for null parameter {parameterDisplay} but no exception was thrown");
}));
if (ex.InnerException is ArgumentNullException argumentNullException)
{
Assert.AreEqual(parameters[i].Name, argumentNullException.ParamName);
}
else
{
throw new Exception($"Thrown argument for {parameterDisplay} was '{ex.InnerException?.GetType().Name}' not {nameof(ArgumentNullException)}.", ex.InnerException);
}
}
}
}
}
}

public static IConstructorTest Use<T>(this IConstructorTest test, T value)
{
if (test is Test internalTest)
{
var newTest = new Test(internalTest);
newTest.SpecifiedValues.Add(typeof(T), value);
return newTest;
}
else
{
throw new ArgumentException("Argument not expected type", nameof(test));
}
}

public static IConstructorTest BuildArgumentNullExceptionsTest<T>()
=> new Test(typeof(T));

public static void AssertArgumentNullExceptions<T>()
=> BuildArgumentNullExceptionsTest<T>().Run();
}
}
20 changes: 20 additions & 0 deletions Moq.AutoMock.Tests/DescribeCombiningTypes.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq.AutoMock.Resolvers;
using Moq.AutoMock.Tests.Util;
using System;
using System.Linq;

namespace Moq.AutoMock.Tests
{
Expand Down Expand Up @@ -38,6 +41,23 @@ public void Combine_method_should_maintain_setups()
Assert.AreEqual("42", mocker.Get<IBaseInterface>().Foo());
Assert.AreEqual("42", mocker.Get<IDerivedInterface>().Foo());
}

[TestMethod]
public void Combine_throws_on_null_type()
{
AutoMocker mocker = new();
var e = Assert.ThrowsException<ArgumentNullException>(() => mocker.Combine(null!));
Assert.AreEqual("type", e.ParamName);
}

[TestMethod]
public void It_throws_if_cache_is_not_registered()
{
AutoMocker mocker = new();
mocker.Resolvers.Remove(mocker.Resolvers.OfType<CacheResolver>().Single());

Assert.ThrowsException<InvalidOperationException>(() => mocker.Combine(typeof(object), typeof(object)));
}
}

public interface IBaseInterface
Expand Down
16 changes: 10 additions & 6 deletions Moq.AutoMock.Tests/DescribeCreateInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,21 @@ public void It_creates_mock_objects_for_ctor_array_parameters()
WithServiceArray instance = mocker.CreateInstance<WithServiceArray>();
IService2[] services = instance.Services;
Assert.IsNotNull(services);
Assert.IsFalse(services.Any());
Assert.AreEqual(1, services.Length);
Assert.IsTrue(services[0] is IService2);
}

[TestMethod]
public void It_creates_mock_objects_for_ctor_array_parameters_with_elements()
{
var mocker = new AutoMocker();
mocker.Use(new Mock<IService2>());
var expectedService = new Mock<IService2>();
mocker.Use(expectedService);
WithServiceArray instance = mocker.CreateInstance<WithServiceArray>();
IService2[] services = instance.Services;
Assert.IsNotNull(services);
Assert.IsTrue(services.Any());
Assert.AreEqual(1, services.Length);
Assert.AreEqual(expectedService.Object, services[0]);
}

[TestMethod]
Expand Down Expand Up @@ -113,13 +116,13 @@ public void It_creates_object_with_2_first_level_dependencies()
{
var mocker = new AutoMocker();
var instance = mocker.CreateInstance<With2ClassDependencies>();

var dependency1 = instance.WithService;
Assert.IsNotNull(dependency1);
Assert.IsInstanceOfType(dependency1, typeof(WithService));
Assert.IsInstanceOfType(Mock.Get(dependency1), typeof(Mock<WithService>));
Assert.AreSame(dependency1, mocker.Get<WithService>());

var dependency2 = instance.With3Parameters;
Assert.IsNotNull(dependency2);
Assert.IsInstanceOfType(dependency2, typeof(With3Parameters));
Expand All @@ -146,7 +149,8 @@ public void It_throws_when_creating_object_with_recursive_dependency()
// I could see this changing to something else in the future, like null. Right now, it seems
// best to cause early failure to clarify what went wrong. Also, returning null "allows" the
// behavior, so it's easier to move that direction later without breaking backward compatibility.
Assert.ThrowsException<InvalidOperationException>(mocker.CreateInstance<WithRecursiveDependency>);
ArgumentException e = Assert.ThrowsException<ArgumentException>(mocker.CreateInstance<WithRecursiveDependency>);
Assert.IsTrue(e.Message.StartsWith($"Did not find a best constructor for `{typeof(WithRecursiveDependency)}`"));
}
}
}
14 changes: 4 additions & 10 deletions Moq.AutoMock.Tests/DescribeGetMock.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Castle.DynamicProxy;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq.AutoMock.Tests.Util;

Expand Down Expand Up @@ -39,14 +40,6 @@ public void It_creates_mock_from_class_from_type_parameter()
Assert.IsNotNull(mock);
}


[TestMethod]
public void It_fails_mocking_abstract_with_protected_ctor()
{
var mocker = new AutoMocker();
Assert.ThrowsException<ArgumentException>(() => mocker.GetMock<ProtectedConstructor1>());
}

[TestMethod]
public void It_allows_protected_abstract_mock_when_overriden()
{
Expand All @@ -57,10 +50,11 @@ public void It_allows_protected_abstract_mock_when_overriden()
}

[TestMethod]
public void It_fails_getting_mocked_object_with_protected_ctor()
public void It_gets_mocked_object_with_protected_ctor()
{
var mocker = new AutoMocker();
Assert.ThrowsException<ArgumentException>(() => mocker.Get<ProtectedConstructor1>());
var mock = mocker.GetMock<ProtectedConstructor1>();
Assert.ThrowsException<InvalidProxyConstructorArgumentsException>(() => mock.Object);
}

[TestMethod]
Expand Down
57 changes: 55 additions & 2 deletions Moq.AutoMock.Tests/DescribeResolvedObjects.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq.AutoMock.Resolvers;
using Moq.AutoMock.Tests.Util;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Moq.AutoMock.Tests
{
Expand Down Expand Up @@ -43,7 +45,7 @@ public void ResolvedObjects_includes_created_primitive_arrays()

Assert.AreEqual(1, resolved.Count);
var resolvedArray = resolved[typeof(string[])] as string[];
Assert.AreEqual(0, resolvedArray?.Length);
Assert.AreEqual(0, resolvedArray!.Length);
}

[TestMethod]
Expand All @@ -58,8 +60,59 @@ public void ResolvedObjects_includes_created_service_arrays()

Assert.AreEqual(2, resolved.Count);
var resolvedArray = resolved[typeof(IService2[])] as IService2[];
Assert.AreEqual(1, resolvedArray?.Length);
Assert.AreEqual(1, resolvedArray!.Length);
Assert.AreEqual(service, resolvedArray![0]);
}

[TestMethod]
public void ResolvedObjects_custom_resolver_providing_value_prevents_subsequent_resolver_from_being_invoked()
{
AutoMocker mocker = new();
object singleton = new();
mocker.Resolvers.Clear();
mocker.Resolvers.Add(new SingletonResolver<object>(singleton));
mocker.Resolvers.Add(new ThrowingResolver());

object resolved = mocker.Get<object>();
Assert.AreEqual(singleton, resolved);
}

[TestMethod]
public void ResolvedObjects_custom_resolver_can_prempt_cache_resolver()
{
object singleton = new();
object used = new();
AutoMocker mocker = new();
mocker.Use(used);
mocker.Resolvers.Insert(0, new SingletonResolver<object>(singleton));

object resolved = mocker.Get<object>();
Assert.AreEqual(singleton, resolved);
}

[ExcludeFromCodeCoverage]
private class ThrowingResolver : IMockResolver
{
public void Resolve(MockResolutionContext context)
=> throw new NotImplementedException();
}

private class SingletonResolver<T> : IMockResolver
{
public SingletonResolver(T value)
{
Value = value;
}

public T Value { get; }

public void Resolve(MockResolutionContext context)
{
if (context.RequestType == typeof(T))
{
context.Value = Value;
}
}
}
}
}
17 changes: 5 additions & 12 deletions Moq.AutoMock.Tests/DescribeSetupWithAny.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,32 +42,25 @@ public void When_method_is_not_found_it_throws()

string expectedMessage =
new MissingMethodException(typeof(IService1).Name, "Unknown Method").Message;
try
{
mock.SetupWithAny("Unknown Method");

}
catch (MissingMethodException ex)
when (ex.Message == expectedMessage)
{ }

var ex = Assert.ThrowsException<MissingMethodException>(() => mock.SetupWithAny("Unknown Method"));
Assert.AreEqual(expectedMessage, ex.Message);
}

[TestMethod]
[ExpectedException(typeof(AmbiguousMatchException))]
public void When_void_method_is_overloaded_it_throws()
{
Mock<IService7> mock = new();

mock.SetupWithAny(nameof(IService7.Void));
Assert.ThrowsException<AmbiguousMatchException>(() => mock.SetupWithAny(nameof(IService7.Void)));
}

[TestMethod]
[ExpectedException(typeof(AmbiguousMatchException))]
public void When_method_is_overloaded_it_throws()
{
Mock<IService7> mock = new();

mock.SetupWithAny(nameof(IService7.ReturnValue));
Assert.ThrowsException<AmbiguousMatchException>(() => mock.SetupWithAny(nameof(IService7.ReturnValue)));
}

[TestMethod]
Expand Down
Loading