[dave@davesquared.net: squashed into single commit for easier rebase]
This commit is contained in:
Родитель
9509dae3ed
Коммит
ff82035229
|
@ -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,
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче