[dave@davesquared.net: squashed into single commit for easier rebase]
This commit is contained in:
Alexandr Nikitin 2013-06-20 22:15:29 +03:00 коммит произвёл David Tchepak
Родитель 9509dae3ed
Коммит ff82035229
28 изменённых файлов: 659 добавлений и 10 удалений

Просмотреть файл

@ -71,6 +71,8 @@
<Compile Include="ArgumentInvocationFromMatchers.cs" />
<Compile Include="ArgDoFromMatcher.cs" />
<Compile Include="AutoValuesForSubs.cs" />
<Compile Include="SubstituteTests.cs" />
<Compile Include="SubstituteExtensionsTests.cs" />
<Compile Include="FieldReports\CallingIntoNewSubWithinReturns.cs" />
<Compile Include="FieldReports\Issue110_CustomExceptions.cs" />
<Compile Include="FieldReports\Issue_ArgumentCheckOfOptionalParameter.cs" />

Просмотреть файл

@ -0,0 +1,247 @@
using System;
using NSubstitute.Exceptions;
using NUnit.Framework;
namespace NSubstitute.Acceptance.Specs
{
[TestFixture]
public class SubstituteExtensionsTests
{
[TestFixture]
public class CallBaseForTests
{
[Test]
public void Given_SubstituteForInterface_When_MethodIsCalled_Then_ShouldThrowException()
{
var testAbstractClass = Substitute.For<ITestInterface>();
testAbstractClass.CallBaseFor().TestMethod();
Assert.Throws<CouldNotCallBaseException>(testAbstractClass.TestMethod);
}
[Test]
public void Given_SubstituteForAbstractClass_When_AbstractMethodIsCalled_Then_ShouldThrowException()
{
var testAbstractClass = Substitute.For<TestAbstractClass>();
testAbstractClass.CallBaseFor().AbstractMethod();
Assert.Throws<CouldNotCallBaseException>(testAbstractClass.AbstractMethod);
}
[Test]
public void Given_SubstituteForAction_When_ActionIsCalled_Then_ShouldThrowException()
{
var action = Substitute.For<Action>();
action.CallBaseFor().Invoke();
Assert.Throws<CouldNotCallBaseException>(() => action());
}
[Test]
public void Given_SubstituteForFunc_When_FuncIsCalled_Then_ShouldThrowException()
{
var func = Substitute.For<Func<int>>();
func.CallBaseFor().Invoke();
Assert.Throws<CouldNotCallBaseException>(() => func());
}
[Test]
public void Given_SubstituteForEventHandler_When_EventHandlerIsCalled_Then_ShouldThrowException()
{
var eventHandler = Substitute.For<EventHandler>();
eventHandler.CallBaseFor().Invoke(Arg.Any<object>(), Arg.Any<EventArgs>());
Assert.Throws<CouldNotCallBaseException>(() => eventHandler.Invoke(null, null));
}
[Test]
public void
Given_SubstituteForAbstractClass_When_MethodWithImplementationIsCalled_Then_ShouldCallBaseImplementation
()
{
var testAbstractClass = Substitute.For<TestAbstractClass>();
testAbstractClass.CallBaseFor().MethodWithImplementation(Arg.Any<int>());
Assert.That(testAbstractClass.MethodWithImplementation(1), Is.EqualTo(1));
}
[Test]
public void Given_SubstituteForClass_When_VirtualMethodIsCalled_Then_ShouldCallBaseImplementation()
{
var testClass = Substitute.For<TestClass>();
testClass.CallBaseFor().VirtualMethod();
testClass.VirtualMethod();
Assert.That(testClass.CalledTimes, Is.EqualTo(1));
}
[Test]
public void Given_SubstituteForClass_When_VirtualMethodIsSetupTwice_Then_ShouldSetupCorrectly()
{
var testClass = Substitute.For<TestClass>();
testClass.CallBaseFor().VirtualMethod();
testClass.CallBaseFor().VirtualMethod();
}
[Test]
public void Given_SubstituteForClass_When_AbstractMethodIsCalled_Then_ShouldCallBaseImplementation()
{
var testClass = Substitute.For<TestClass>();
testClass.CallBaseFor().AbstractMethod();
testClass.AbstractMethod();
Assert.That(testClass.CalledTimes, Is.EqualTo(1));
}
[Test]
public void
Given_SubstituteForClass_When_NotVirtualMethodIsSetup_And_NotVirtualMethodIsCalled_Then_ShouldCallBaseImplementationTwice
()
{
var testClass = Substitute.For<TestClass>();
testClass.CallBaseFor().NotVirtualMethodReturnsSameInt(Arg.Any<int>());
testClass.NotVirtualMethodReturnsSameInt(1);
Assert.That(testClass.CalledTimes, Is.EqualTo(2));
}
[Test]
public void Given_SubstituteForClass_When_VirtualMethodIsCalledTwice_Then_ShouldCallBaseImplementationTwice()
{
var testClass = Substitute.For<TestClass>();
testClass.CallBaseFor().VirtualMethod();
testClass.VirtualMethod();
testClass.VirtualMethod();
Assert.That(testClass.CalledTimes, Is.EqualTo(2));
}
[Test]
public void
Given_SubstituteForClass_When_VirtualMethodsReturnValueIsOverwritten_Then_ShouldNotCallBaseImplementation
()
{
var testClass = Substitute.For<TestClass>();
testClass.CallBaseFor().VirtualMethodReturnsSameInt(Arg.Any<int>());
testClass.VirtualMethodReturnsSameInt(Arg.Any<int>()).Returns(1);
Assert.That(testClass.CalledTimes, Is.EqualTo(0));
}
[Test]
public void
Given_SubstituteForClass_And_ReturnValueIsOverwritten_When_VirtualMethodIsCalled_Then_ShouldReturnOverwrittenValue
()
{
var testClass = Substitute.For<TestClass>();
testClass.CallBaseFor().VirtualMethodReturnsSameInt(Arg.Any<int>());
testClass.VirtualMethodReturnsSameInt(Arg.Any<int>()).Returns(2);
Assert.That(testClass.VirtualMethodReturnsSameInt(1), Is.EqualTo(2));
}
[Test]
[Pending, Explicit]
// test fails only when executed with all tests, don't understand why by now.
public void
Given_SubstituteForClass_And_ObjectReturnValueIsOverwritten_When_VirtualMethodIsCalled_Then_ShouldReturnOverwrittenValue
()
{
var testClass = Substitute.For<TestClass>();
testClass.CallBaseFor().VirtualMethodReturnsSameObject(Arg.Any<object>());
var objectToReturn = new object();
testClass.VirtualMethodReturnsSameObject(Arg.Any<object>()).Returns(objectToReturn);
Assert.That(testClass.VirtualMethodReturnsSameObject(new object()), Is.EqualTo(objectToReturn));
}
[Test]
public void
Given_SubstituteForClass_And_SubstituteDoesNotOverwriteReturn_When_VirtualMethodIsCalled_Then_ShouldCallBaseImplementation
()
{
var testClass = Substitute.For<TestClass>();
var objectCallBase = new object();
testClass.CallBaseFor().VirtualMethodReturnsSameObject(Arg.Is(objectCallBase));
var objectCallSubstitute = new object();
testClass.VirtualMethodReturnsSameObject(Arg.Is(objectCallSubstitute)).Returns(new object());
Assert.That(testClass.VirtualMethodReturnsSameObject(objectCallBase), Is.EqualTo(objectCallBase));
}
[Test]
public void
Given_SubstituteForClass_And_SubstituteDoesOverwriteReturn_When_VirtualMethodIsCalled_Then_ShouldReturnOverwrittenValue
()
{
var testClass = Substitute.For<TestClass>();
var objectCallBase = new object();
testClass.CallBaseFor().VirtualMethodReturnsSameObject(Arg.Is(objectCallBase));
var objectToReturn = new object();
testClass.VirtualMethodReturnsSameObject(Arg.Is(objectCallBase)).Returns(objectToReturn);
Assert.That(testClass.VirtualMethodReturnsSameObject(objectCallBase), Is.EqualTo(objectToReturn));
}
[Test]
public void
Given_SubstituteForClass_And_VirtualMethodReturnValueForSpecifiedArg_When_VirtualMethodIsCalledWithSpecifiedArg_Then_ShouldReturnOverwrittenValue
()
{
var testClass = Substitute.For<TestClass>();
testClass.CallBaseFor().VirtualMethodReturnsSameInt(Arg.Any<int>());
testClass.VirtualMethodReturnsSameInt(Arg.Is(1)).Returns(2);
// substitute has higher priority than base implementation
Assert.That(testClass.VirtualMethodReturnsSameInt(1), Is.EqualTo(2));
Assert.That(testClass.CalledTimes, Is.EqualTo(0));
}
[Test]
public void
Given_SubstituteForClass_And_VirtualMethodReturnValueForSpecifiedArg_When_VirtualMethodIsCalledWithoutSpecifiedArg_Then_ShouldCallBaseImplementation
()
{
var testClass = Substitute.For<TestClass>();
testClass.CallBaseFor().VirtualMethodReturnsSameInt(Arg.Any<int>());
testClass.VirtualMethodReturnsSameInt(Arg.Is(1)).Returns(2);
Assert.That(testClass.VirtualMethodReturnsSameInt(3), Is.EqualTo(3));
Assert.That(testClass.CalledTimes, Is.EqualTo(1));
}
public interface ITestInterface
{
void TestMethod();
}
public abstract class TestAbstractClass
{
public abstract void AbstractMethod();
public int MethodWithImplementation(int i)
{
return i;
}
}
public class TestClass : TestAbstractClass
{
public int CalledTimes { get; set; }
public virtual int VirtualMethodReturnsSameInt(int i)
{
CalledTimes++;
return i;
}
public virtual object VirtualMethodReturnsSameObject(object o)
{
CalledTimes++;
return o;
}
public virtual void VirtualMethod()
{
CalledTimes++;
}
public int NotVirtualMethodReturnsSameInt(int i)
{
CalledTimes++;
return i;
}
public override void AbstractMethod()
{
CalledTimes++;
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,168 @@
using System;
using NSubstitute.Exceptions;
using NUnit.Framework;
namespace NSubstitute.Acceptance.Specs
{
[TestFixture]
public class SubstituteTests
{
[TestFixture]
public class ForTests
{
[TestFixture]
public class ThatCallsBaseByDefaultTests
{
[Test]
public void Given_SubstituteForInterface_When_MethodIsCalled_Then_ShouldThrowException()
{
var testAbstractClass = Substitute.For<ITestInterface>(ThatCallsBase.ByDefault);
Assert.Throws<CouldNotCallBaseException>(testAbstractClass.TestMethod);
}
[Test]
public void Given_SubstituteForAbstractClass_When_AbstractMethodIsCalled_Then_ShouldThrowException()
{
var testAbstractClass = Substitute.For<TestAbstractClass>(ThatCallsBase.ByDefault);
Assert.Throws<CouldNotCallBaseException>(testAbstractClass.AbstractMethod);
}
[Test]
public void Given_SubstituteForAction_When_ActionIsCalled_Then_ShouldThrowException()
{
var action = Substitute.For<Action>(ThatCallsBase.ByDefault);
Assert.Throws<CouldNotCallBaseException>(() => action());
}
[Test]
public void Given_SubstituteForFunc_When_FuncIsCalled_Then_ShouldThrowException()
{
var func = Substitute.For<Func<int>>(ThatCallsBase.ByDefault);
Assert.Throws<CouldNotCallBaseException>(() => func());
}
[Test]
public void Given_SubstituteForEventHandler_When_EventHandlerIsCalled_Then_ShouldThrowException()
{
var eventHandler = Substitute.For<EventHandler>(ThatCallsBase.ByDefault);
Assert.Throws<CouldNotCallBaseException>(() => eventHandler.Invoke(null, null));
}
[Test]
public void Given_SubstituteForAbstractClass_When_MethodWithImplementationIsCalled_Then_ShouldCallBaseImplementation()
{
var testAbstractClass = Substitute.For<TestAbstractClass>(ThatCallsBase.ByDefault);
Assert.That(testAbstractClass.MethodWithImplementation(1), Is.EqualTo(1));
}
[Test]
public void Given_SubstituteForClass_When_VirtualMethodIsCalled_Then_ShouldCallBaseImplementation()
{
var testClass = Substitute.For<TestClass>(ThatCallsBase.ByDefault);
testClass.VirtualMethod();
Assert.That(testClass.CalledTimes, Is.EqualTo(1));
}
[Test]
public void Given_SubstituteForClass_When_AbstractMethodIsCalled_Then_ShouldCallBaseImplementation()
{
var testClass = Substitute.For<TestClass>(ThatCallsBase.ByDefault);
testClass.AbstractMethod();
Assert.That(testClass.CalledTimes, Is.EqualTo(1));
}
[Test]
public void Given_SubstituteForClass_When_NotVirtualMethodIsCalled_Then_ShouldCallBaseImplementation()
{
var testClass = Substitute.For<TestClass>(ThatCallsBase.ByDefault);
testClass.NotVirtualMethodReturnsSameInt(1);
Assert.That(testClass.CalledTimes, Is.EqualTo(1));
}
[Test]
public void Given_SubstituteForClass_When_VirtualMethodIsCalledTwice_Then_ShouldCallBaseImplementationTwice()
{
var testClass = Substitute.For<TestClass>(ThatCallsBase.ByDefault);
testClass.VirtualMethod();
testClass.VirtualMethod();
Assert.That(testClass.CalledTimes, Is.EqualTo(2));
}
[Test]
public void Given_SubstituteForClass_When_VirtualMethodsReturnValueIsOverwritten_Then_ShouldNotCallBaseImplementation()
{
var testClass = Substitute.For<TestClass>(ThatCallsBase.ByDefault);
testClass.VirtualMethodReturnsSameInt(Arg.Any<int>()).Returns(1);
Assert.That(testClass.CalledTimes, Is.EqualTo(0));
}
[Test]
public void Given_SubstituteForClass_And_ReturnValueIsOverwritten_When_VirtualMethodIsCalled_Then_ShouldReturnOverwrittenValue()
{
var testClass = Substitute.For<TestClass>(ThatCallsBase.ByDefault);
testClass.VirtualMethodReturnsSameInt(Arg.Any<int>()).Returns(2);
Assert.That(testClass.VirtualMethodReturnsSameInt(1), Is.EqualTo(2));
}
[Test]
public void Given_SubstituteForClass_And_VirtualMethodReturnValueForSpecifiedArg_When_VirtualMethodIsCalledWithSpecifiedArg_Then_ShouldNotCallBaseImplementation()
{
var testClass = Substitute.For<TestClass>(ThatCallsBase.ByDefault);
testClass.VirtualMethodReturnsSameInt(Arg.Is(1)).Returns(2);
Assert.That(testClass.VirtualMethodReturnsSameInt(1), Is.EqualTo(2));
Assert.That(testClass.CalledTimes, Is.EqualTo(0));
}
[Test]
public void Given_SubstituteForClass_And_VirtualMethodReturnValueForSpecifiedArg_When_VirtualMethodIsCalledWithoutSpecifiedArg_Then_ShouldCallBaseImplementation()
{
var testClass = Substitute.For<TestClass>(ThatCallsBase.ByDefault);
testClass.VirtualMethodReturnsSameInt(Arg.Is(1)).Returns(2);
Assert.That(testClass.VirtualMethodReturnsSameInt(3), Is.EqualTo(3));
Assert.That(testClass.CalledTimes, Is.EqualTo(1));
}
public interface ITestInterface
{
void TestMethod();
}
public abstract class TestAbstractClass
{
public abstract void AbstractMethod();
public int MethodWithImplementation(int i)
{
return i;
}
}
public class TestClass : TestAbstractClass
{
public int CalledTimes { get; set; }
public virtual int VirtualMethodReturnsSameInt(int i)
{
CalledTimes++;
return i;
}
public virtual void VirtualMethod()
{
CalledTimes++;
}
public int NotVirtualMethodReturnsSameInt(int i)
{
CalledTimes++;
return i;
}
public override void AbstractMethod()
{
CalledTimes++;
}
}
}
}
}
}

Просмотреть файл

@ -11,6 +11,7 @@ namespace NSubstitute.Specs
public abstract class Concern : ConcernFor<ConfigureCall>
{
protected ICallActions _callActions;
protected ICallBaseSpecifications _callBaseSpecifications;
protected ICallResults _configuredResults;
protected IGetCallSpec _getCallSpec;
protected IReturn _compatibleReturnValue;
@ -20,6 +21,7 @@ namespace NSubstitute.Specs
_configuredResults = mock<ICallResults>();
_callActions = mock<ICallActions>();
_getCallSpec = mock<IGetCallSpec>();
_callBaseSpecifications = mock<ICallBaseSpecifications>();
_compatibleReturnValue = mock<IReturn>();
_compatibleReturnValue.stub(x => x.CanBeAssignedTo(It.IsAny<Type>())).Return(true);
@ -27,7 +29,7 @@ namespace NSubstitute.Specs
public override ConfigureCall CreateSubjectUnderTest()
{
return new ConfigureCall(_configuredResults, _callActions, _getCallSpec);
return new ConfigureCall(_configuredResults, _callActions, _getCallSpec, _callBaseSpecifications);
}
}

Просмотреть файл

@ -29,5 +29,10 @@ namespace NSubstitute.Specs.Infrastructure
{
FactoryMethodUsedToSetRoute = getRoute;
}
public void CallBaseByDefault(bool callBaseByDefault)
{
throw new NotImplementedException();
}
}
}

Просмотреть файл

@ -15,14 +15,16 @@ namespace NSubstitute.Core
private readonly IParameterInfo[] _parameterInfos;
private IList<IArgumentSpecification> _argumentSpecifications;
private long? _sequenceNumber;
private readonly Func<object> _baseMethod;
public Call(MethodInfo methodInfo, object[] arguments, object target, IList<IArgumentSpecification> argumentSpecsForCall)
public Call(MethodInfo methodInfo, object[] arguments, object target, IList<IArgumentSpecification> argumentSpecsForCall, Func<object> baseMethod = null)
{
_methodInfo = methodInfo;
_arguments = arguments;
_target = target;
_parameterInfos = GetParameterInfosFrom(_methodInfo);
_argumentSpecifications = argumentSpecsForCall;
_baseMethod = baseMethod;
}
public Call(MethodInfo methodInfo, object[] arguments, object target, IParameterInfo[] parameterInfos)
@ -70,6 +72,12 @@ namespace NSubstitute.Core
return _sequenceNumber.Value;
}
public object CallBase()
{
if (_baseMethod != null) return _baseMethod();
throw new CouldNotCallBaseException(_methodInfo);
}
public Type GetReturnType()
{
return _methodInfo.ReturnType;

Просмотреть файл

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Linq;
namespace NSubstitute.Core
{
public class CallBaseSpecifications : ICallBaseSpecifications
{
readonly List<ICallSpecification> _callSpecifications = new List<ICallSpecification>();
public void Add(ICallSpecification callSpecification)
{
_callSpecifications.Add(callSpecification);
}
public bool DoesCallBase(ICall callInfo)
{
return _callSpecifications.Any(cs => cs.IsSatisfiedBy(callInfo));
}
}
}

Просмотреть файл

@ -16,10 +16,10 @@ namespace NSubstitute.Core
_context = context;
}
public ICall Create(MethodInfo methodInfo, object[] arguments, object target)
public ICall Create(MethodInfo methodInfo, object[] arguments, object target, Func<object> baseMethod = null)
{
var argSpecs = (methodInfo.GetParameters().Length == 0) ? EmptyList() : _context.DequeueAllArgumentSpecifications();
return new Call(methodInfo, arguments, target, argSpecs);
return new Call(methodInfo, arguments, target, argSpecs, baseMethod);
}
private IList<IArgumentSpecification> EmptyList()

Просмотреть файл

@ -36,6 +36,11 @@ namespace NSubstitute.Core
_currentRoute = route;
}
public void CallBaseByDefault(bool callBaseByDefault)
{
_substituteState.CallBaseByDefault = callBaseByDefault;
}
public void ClearReceivedCalls()
{
_receivedCalls.Clear();

Просмотреть файл

@ -7,12 +7,14 @@ namespace NSubstitute.Core
private readonly ICallResults _configuredResults;
private readonly ICallActions _callActions;
private readonly IGetCallSpec _getCallSpec;
private readonly ICallBaseSpecifications _callBaseSpecifications;
public ConfigureCall(ICallResults configuredResults, ICallActions callActions, IGetCallSpec getCallSpec)
public ConfigureCall(ICallResults configuredResults, ICallActions callActions, IGetCallSpec getCallSpec, ICallBaseSpecifications callBaseSpecifications)
{
_configuredResults = configuredResults;
_callActions = callActions;
_getCallSpec = getCallSpec;
_callBaseSpecifications = callBaseSpecifications;
}
public ConfiguredCall SetResultForLastCall(IReturn valueToReturn, MatchArgs matchArgs)

Просмотреть файл

@ -15,5 +15,6 @@ namespace NSubstitute.Core
IList<IArgumentSpecification> GetArgumentSpecifications();
void AssignSequenceNumber(long number);
long GetSequenceNumber();
object CallBase();
}
}

Просмотреть файл

@ -0,0 +1,8 @@
namespace NSubstitute.Core
{
public interface ICallBaseSpecifications
{
void Add(ICallSpecification callSpecification);
bool DoesCallBase(ICall callInfo);
}
}

Просмотреть файл

@ -11,5 +11,6 @@ namespace NSubstitute.Core
void ClearReceivedCalls();
IEnumerable<ICall> ReceivedCalls();
void SetRoute(Func<ISubstituteState, IRoute> getRoute);
void CallBaseByDefault(bool callBaseByDefault);
}
}

Просмотреть файл

@ -5,6 +5,7 @@ namespace NSubstitute.Core
public interface ISubstituteFactory
{
object Create(Type[] typesToProxy, object[] constructorArguments);
object Create(Type[] typesToProxy, object[] constructorArguments, bool callBaseByDefault);
ICallRouter GetCallRouterCreatedFor(object substitute);
}
}

Просмотреть файл

@ -12,6 +12,8 @@ namespace NSubstitute.Core
ICallResults CallResults { get; }
ICallSpecificationFactory CallSpecificationFactory { get; }
ICallActions CallActions { get; }
ICallBaseSpecifications CallBaseSpecifications { get; }
bool CallBaseByDefault { get; set; }
SequenceNumberGenerator SequenceNumberGenerator { get; }
IConfigureCall ConfigureCall { get; }
IEventHandlerRegistry EventHandlerRegistry { get; }

Просмотреть файл

@ -18,9 +18,15 @@ namespace NSubstitute.Core
_callRouterResolver = callRouterResolver;
}
public object Create(Type[] typesToProxy, object[] constructorArguments)
public object Create(Type[] typesToProxy, object[] constructorArguments)
{
return Create(typesToProxy, constructorArguments, callBaseByDefault: false);
}
public object Create(Type[] typesToProxy, object[] constructorArguments, bool callBaseByDefault)
{
var callRouter = _callRouterFactory.Create(_context);
callRouter.CallBaseByDefault(callBaseByDefault);
var primaryProxyType = GetPrimaryProxyType(typesToProxy);
var additionalTypes = typesToProxy.Where(x => x != primaryProxyType).ToArray();
var proxy = _proxyFactory.GenerateProxy(callRouter, primaryProxyType, additionalTypes, constructorArguments);

Просмотреть файл

@ -11,6 +11,8 @@ namespace NSubstitute.Core
public ICallResults CallResults { get; private set; }
public ICallSpecificationFactory CallSpecificationFactory { get; private set; }
public ICallActions CallActions { get; private set; }
public ICallBaseSpecifications CallBaseSpecifications { get; private set; }
public bool CallBaseByDefault { get; set; }
public SequenceNumberGenerator SequenceNumberGenerator { get; private set; }
public IConfigureCall ConfigureCall { get; private set; }
public IEventHandlerRegistry EventHandlerRegistry { get; private set; }
@ -29,10 +31,11 @@ namespace NSubstitute.Core
CallResults = new CallResults(callInfoFactory);
CallSpecificationFactory = CallSpecificationFactoryFactoryYesThatsRight.CreateCallSpecFactory();
CallActions = new CallActions(callInfoFactory);
CallBaseSpecifications = new CallBaseSpecifications();
var getCallSpec = new GetCallSpec(callStack, PendingSpecification, CallSpecificationFactory, CallActions);
ConfigureCall = new ConfigureCall(CallResults, CallActions, getCallSpec);
ConfigureCall = new ConfigureCall(CallResults, CallActions, getCallSpec, CallBaseSpecifications);
EventHandlerRegistry = new EventHandlerRegistry();
AutoValueProviders = new IAutoValueProvider[] {
new AutoSubstituteProvider(substituteFactory),

Просмотреть файл

@ -29,6 +29,5 @@ namespace NSubstitute.Core
_callRouter.SetRoute(x => _routeFactory.DoWhenCalled(x, callbackWithArguments, _matchArgs));
_call(_substitute);
}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.Serialization;
namespace NSubstitute.Exceptions
{
public class CouldNotCallBaseException : SubstituteException
{
private const string WhatProbablyWentWrong = "Make sure you substituted for a class rather than abstraction (interface, abstract class etc).";
public CouldNotCallBaseException(MethodInfo methodInfo) : base(DescribeProblem(methodInfo) + "\n\n" + WhatProbablyWentWrong) { }
protected CouldNotCallBaseException(SerializationInfo info, StreamingContext context) : base(info, context) { }
private static string DescribeProblem(MethodInfo member)
{
return string.Format("Can not call base implementation for {0}.{1}.", member.DeclaringType.Name, member.Name);
}
}
}

Просмотреть файл

@ -88,6 +88,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Arg.cs" />
<Compile Include="ThatCallsBase.cs" />
<Compile Include="Core\Argument.cs" />
<Compile Include="Core\Arguments\AnyArgumentMatcher.cs" />
<Compile Include="Core\Arguments\ArgumentAction.cs" />
@ -115,6 +116,7 @@
<Compile Include="Core\Arguments\ParamsArgumentSpecificationFactory.cs" />
<Compile Include="Core\Arguments\SuppliedArgumentSpecifications.cs" />
<Compile Include="Core\Arguments\SuppliedArgumentSpecificationsFactory.cs" />
<Compile Include="Core\CallBaseSpecifications.cs" />
<Compile Include="Core\CallFactory.cs" />
<Compile Include="Core\CallSpecAndTarget.cs" />
<Compile Include="Core\CallSpecificationFactoryFactoryYesThatsRight.cs" />
@ -124,6 +126,7 @@
<Compile Include="Core\Arguments\IArgumentEqualsSpecificationFactory.cs" />
<Compile Include="Core\Extensions.cs" />
<Compile Include="Core\GetCallSpec.cs" />
<Compile Include="Core\ICallBaseSpecifications.cs" />
<Compile Include="Core\ICallRouterProvider.cs" />
<Compile Include="Core\IGetCallSpec.cs" />
<Compile Include="Core\SequenceChecking\InstanceTracker.cs" />
@ -146,6 +149,7 @@
<Compile Include="Exceptions\ArgumentIsNotOutOrRefException.cs" />
<Compile Include="Exceptions\ArgumentSetWithIncompatibleValueException.cs" />
<Compile Include="Exceptions\CallSequenceNotFoundException.cs" />
<Compile Include="Exceptions\CouldNotCallBaseException.cs" />
<Compile Include="Exceptions\CannotCreateEventArgsException.cs" />
<Compile Include="Exceptions\CannotReturnNullForValueType.cs" />
<Compile Include="Exceptions\CouldNotSetReturnException.cs" />
@ -188,6 +192,8 @@
<Compile Include="Core\IReturn.cs" />
<Compile Include="Core\PropertyCallFormatter.cs" />
<Compile Include="Core\ReflectionExtensions.cs" />
<Compile Include="Routing\Handlers\ReturnBaseResultCallHandler.cs" />
<Compile Include="Routing\Handlers\SetBaseForCallHandler.cs" />
<Compile Include="Routing\IRouteFactory.cs" />
<Compile Include="Routing\Route.cs" />
<Compile Include="Exceptions\ArgumentNotFoundException.cs" />

Просмотреть файл

@ -1,3 +1,4 @@
using System;
using Castle.DynamicProxy;
using NSubstitute.Core;
@ -9,7 +10,19 @@ namespace NSubstitute.Proxies.CastleDynamicProxy
public virtual ICall Map(IInvocation castleInvocation)
{
return CallFactory.Create(castleInvocation.Method, castleInvocation.Arguments, castleInvocation.Proxy);
Func<object> baseMethod = null;
if (castleInvocation.InvocationTarget != null &&
castleInvocation.MethodInvocationTarget.IsAbstract != true)
{
baseMethod = () =>
{
castleInvocation.Proceed();
return castleInvocation.ReturnValue;
};
}
return CallFactory.Create(castleInvocation.Method, castleInvocation.Arguments, castleInvocation.Proxy, baseMethod);
}
}
}

Просмотреть файл

@ -0,0 +1,26 @@
using NSubstitute.Core;
namespace NSubstitute.Routing.Handlers
{
public class ReturnBaseResultCallHandler : ICallHandler
{
private readonly ISubstituteState _substituteState;
private readonly ICallBaseSpecifications _callBaseSpecifications;
public ReturnBaseResultCallHandler(ISubstituteState substituteState, ICallBaseSpecifications callBaseSpecifications)
{
_substituteState = substituteState;
_callBaseSpecifications = callBaseSpecifications;
}
public RouteAction Handle(ICall call)
{
if (_callBaseSpecifications.DoesCallBase(call) || _substituteState.CallBaseByDefault)
{
return RouteAction.Return(call.CallBase());
}
return RouteAction.Continue();
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
using NSubstitute.Core;
namespace NSubstitute.Routing.Handlers
{
public class SetBaseForCallHandler : ICallHandler
{
private readonly ICallSpecificationFactory _callSpecificationFactory;
private readonly ICallBaseSpecifications _callBaseSpecifications;
private readonly MatchArgs _matchArgs;
public SetBaseForCallHandler(ICallSpecificationFactory callSpecificationFactory, ICallBaseSpecifications callBaseSpecifications, MatchArgs matchArgs)
{
_callSpecificationFactory = callSpecificationFactory;
_callBaseSpecifications = callBaseSpecifications;
_matchArgs = matchArgs;
}
public RouteAction Handle(ICall call)
{
var callSpec = _callSpecificationFactory.CreateFrom(call, _matchArgs);
_callBaseSpecifications.Add(callSpec);
return RouteAction.Continue();
}
}
}

Просмотреть файл

@ -5,6 +5,7 @@ namespace NSubstitute.Routing
{
public interface IRouteFactory
{
IRoute CallBase(ISubstituteState state, MatchArgs matchArgs);
IRoute CallQuery(ISubstituteState state);
IRoute CheckReceivedCalls(ISubstituteState state, MatchArgs matchArgs, Quantity requiredQuantity);
IRoute DoWhenCalled(ISubstituteState state, Action<CallInfo> doAction, MatchArgs matchArgs);

Просмотреть файл

@ -6,6 +6,16 @@ namespace NSubstitute.Routing
{
public class RouteFactory : IRouteFactory
{
public IRoute CallBase(ISubstituteState state, MatchArgs matchArgs)
{
return new Route(new ICallHandler[] {
new ClearLastCallRouterHandler(state.SubstitutionContext)
, new ClearUnusedCallSpecHandler(state)
, new SetBaseForCallHandler(state.CallSpecificationFactory, state.CallBaseSpecifications, matchArgs)
, ReturnDefaultForReturnTypeHandler()
});
}
public IRoute CallQuery(ISubstituteState state)
{
return new Route(new ICallHandler[] {
@ -62,6 +72,7 @@ namespace NSubstitute.Routing
, new PropertySetterHandler(new PropertyHelper(), state.ConfigureCall)
, new DoActionsCallHandler(state.CallActions)
, new ReturnConfiguredResultHandler(state.CallResults)
, new ReturnBaseResultCallHandler(state, state.CallBaseSpecifications)
, new ReturnAutoValueForThisAndSubsequentCallsHandler(state.AutoValueProviders, state.ConfigureCall)
, ReturnDefaultForReturnTypeHandler()
});

Просмотреть файл

@ -22,6 +22,21 @@ namespace NSubstitute
return (T) For(new[] {typeof(T)}, constructorArguments);
}
/// <summary>
/// Substitute for an interface or class.
/// <para>Be careful when specifying a class, as all non-virtual members will actually be executed. Only virtual members
/// can be recorded or have return values specified.</para>
/// </summary>
/// <typeparam name="T">The type of interface or class to substitute.</typeparam>
/// <param name="callBaseBehavior">Represents default substitute behavior. If <see cref="ThatCallsBase.ByDefault"/> is passed then the substitute calls base implementation by default. </param>
/// <param name="constructorArguments">Arguments required to construct a class being substituted. Not required for interfaces or classes with default constructors.</param>
/// <returns>A substitute for the interface or class.</returns>
public static T For<T>(ThatCallsBase callBaseBehavior, params object[] constructorArguments)
where T : class
{
return (T)For(new[] { typeof(T) }, constructorArguments, callBaseBehavior == ThatCallsBase.ByDefault);
}
/// <summary>
/// <para>Substitute for multiple interfaces or a class that implements an interface. At most one class can be specified.</para>
/// <para>Be careful when specifying a class, as all non-virtual members will actually be executed. Only virtual members
@ -70,5 +85,20 @@ namespace NSubstitute
var substituteFactory = SubstitutionContext.Current.SubstituteFactory;
return substituteFactory.Create(typesToProxy, constructorArguments);
}
/// <summary>
/// <para>Substitute for multiple interfaces or a class that implements multiple interfaces. At most one class can be specified.</para>
/// <para>Be careful when specifying a class, as all non-virtual members will actually be executed. Only virtual members
/// can be recorded or have return values specified.</para>
/// </summary>
/// <param name="typesToProxy">The types of interfaces or a type of class and multiple interfaces the substitute should implement.</param>
/// <param name="constructorArguments">Arguments required to construct a class being substituted. Not required for interfaces or classes with default constructors.</param>
/// <param name="callBaseByDefault">If true the substitute calls base implementation by default if it's not overwritten</param>
/// <returns>A substitute implementing the specified types.</returns>
private static object For(Type[] typesToProxy, object[] constructorArguments, bool callBaseByDefault)
{
var substituteFactory = SubstitutionContext.Current.SubstituteFactory;
return substituteFactory.Create(typesToProxy, constructorArguments, callBaseByDefault);
}
}
}

Просмотреть файл

@ -157,7 +157,7 @@ namespace NSubstitute
router.SetRoute(x => RouteFactory().CheckReceivedCalls(x, MatchArgs.Any, Quantity.Exactly(requiredNumberOfCalls)));
return substitute;
}
/// <summary>
/// Checks this substitute has not received the following call with any arguments.
/// </summary>
@ -224,6 +224,26 @@ namespace NSubstitute
return GetRouterForSubstitute(substitute).ReceivedCalls();
}
/// <summary>
/// Calls base implementation for the specified member called with any args.
/// </summary>
public static T CallBaseWithAnyArgsFor<T>(this T substitute) where T : class
{
var router = GetRouterForSubstitute(substitute);
router.SetRoute(x => RouteFactory().CallBase(x, MatchArgs.Any));
return substitute;
}
/// <summary>
/// Calls base implementation for the specified member.
/// </summary>
public static T CallBaseFor<T>(this T substitute) where T : class
{
var router = GetRouterForSubstitute(substitute);
router.SetRoute(x => RouteFactory().CallBase(x, MatchArgs.AsSpecifiedInCall));
return substitute;
}
private static ICallRouter GetRouterForSubstitute<T>(T substitute)
{
var context = SubstitutionContext.Current;

Просмотреть файл

@ -0,0 +1,18 @@
namespace NSubstitute
{
/// <summary>
/// Represents the substitute behavior regarding base implementation
/// </summary>
public enum ThatCallsBase
{
/// <summary>
/// The substitute calls base implementation only when it's specified explicitly.
/// </summary>
OnlyWhenSpecified = 0,
/// <summary>
/// The substitute calls base implementation by default. Make sure that you substitute for a class.
/// </summary>
ByDefault,
}
}