diff --git a/Source/NSubstitute.Acceptance.Specs/CallBaseForMember.cs b/Source/NSubstitute.Acceptance.Specs/CallBaseForMember.cs index 18de1cf..939b958 100644 --- a/Source/NSubstitute.Acceptance.Specs/CallBaseForMember.cs +++ b/Source/NSubstitute.Acceptance.Specs/CallBaseForMember.cs @@ -4,206 +4,230 @@ using NUnit.Framework; namespace NSubstitute.Acceptance.Specs { + // todo move tests to proper place, add more? public class CallBaseForMember { - [Test] - public void Given_SubstituteForInterface_When_MethodIsCalled_Then_ShouldThrowException() + public class WhenCalledDoCallBase { - var testAbstractClass = Substitute.For(); - Assert.Throws(() => testAbstractClass.CallBaseFor().TestMethod()); + [Test] + public void Given_SubstituteForInterface_When_MethodIsCalled_Then_ShouldThrowException() + { + var testInterface = Substitute.For(); + testInterface.When(x => x.VoidTestMethod()).Do(x => x.CallBase()); + Assert.Throws(testInterface.VoidTestMethod); + } + + [Test] + public void Given_SubstituteForAbstractClass_When_AbstractMethodIsCalled_Then_ShouldThrowException() + { + var testAbstractClass = Substitute.For(); + testAbstractClass.When(x => x.VoidAbstractMethod()).Do(x => x.CallBase()); + Assert.Throws(testAbstractClass.VoidAbstractMethod); + } + + [Test] + public void Given_SubstituteForAction_When_ActionIsCalled_Then_ShouldThrowException() + { + var action = Substitute.For(); + action.When(x => x.Invoke()).Do(x => x.CallBase()); + Assert.Throws(() => action()); + } + + [Test] + public void Given_SubstituteForFunc_When_FuncIsCalled_Then_ShouldThrowException() + { + var func = Substitute.For>(); + func.When(x => x.Invoke()).Do(x => x.CallBase()); + Assert.Throws(() => func()); + } + + [Test] + public void Given_SubstituteForEventHandler_When_EventHandlerIsCalled_Then_ShouldThrowException() + { + var eventHandler = Substitute.For(); + eventHandler.When(x => x.Invoke(Arg.Any(), Arg.Any())).Do(x => x.CallBase()); + Assert.Throws(() => eventHandler(null, null)); + } + + [Test] + public void Given_SubstituteForClass_When_VirtualMethodIsCalled_Then_ShouldCallBaseImplementation() + { + var testClass = Substitute.For(); + testClass.When(x => x.VoidVirtualMethod()).Do(x => x.CallBase()); + testClass.VoidVirtualMethod(); + Assert.That(testClass.CalledTimes, Is.EqualTo(1)); + } + + [Test] + public void Given_SubstituteForClass_When_VirtualMethodIsSetup_ShouldNotCallRealImplementation() + { + var testClass = Substitute.For(); + testClass.When(x => x.VoidVirtualMethod()).Do(x => x.CallBase()); + testClass.When(x => x.VoidVirtualMethod()).Do(x => x.CallBase()); + + Assert.That(testClass.CalledTimes, Is.EqualTo(0)); + } + + [Test] + public void Given_SubstituteForClass_When_VirtualMethodIsSetupTwice_Then_ShouldSetupCorrectly() + { + var testClass = Substitute.For(); + testClass.When(x => x.VoidVirtualMethod()).Do(x => x.CallBase()); + testClass.When(x => x.VoidVirtualMethod()).Do(x => x.CallBase()); + + testClass.VoidVirtualMethod(); + // I doubt that we should call base implementation just once here + // as .When().Do() syntax purpose is to invoke actions and not Return/Callbase + // and just by accident we have two actions that call base implemenation + Assert.That(testClass.CalledTimes, Is.EqualTo(2)); + } + + [Test] + public void Given_SubstituteForClass_When_AbstractMethodIsCalled_Then_ShouldCallBaseImplementation() + { + var testClass = Substitute.For(); + testClass.When(x => x.VoidAbstractMethod()).Do(x => x.CallBase()); + testClass.VoidAbstractMethod(); + Assert.That(testClass.CalledTimes, Is.EqualTo(1)); + } + + [Test] + public void Given_SubstituteForClass_When_VirtualMethodIsCalledTwice_Then_ShouldCallBaseImplementationTwice() + { + var testClass = Substitute.For(); + testClass.When(x => x.VoidVirtualMethod()).Do(x => x.CallBase()); + testClass.VoidVirtualMethod(); + testClass.VoidVirtualMethod(); + Assert.That(testClass.CalledTimes, Is.EqualTo(2)); + } + + [Test] + public void Given_SubstituteForAbstractClass_When_MethodWithImplementationIsCalled_Then_ShouldCallBaseImplementation() + { + var testAbstractClass = Substitute.For(); + testAbstractClass.When(x => x.MethodReturnsSameInt(Arg.Any())).Do(x => x.CallBase()); + // .When().Do() syntax does not return value from base implementation + Assert.That(testAbstractClass.MethodReturnsSameInt(1), Is.EqualTo(default(int))); + // but it calls base implementation + Assert.That(testAbstractClass.CalledTimes, Is.EqualTo(1)); + } } - [Test] - public void Given_SubstituteForAbstractClass_When_AbstractMethodIsCalled_Then_ShouldThrowException() + public class ReturnsCallBase { - var testAbstractClass = Substitute.For(); - Assert.Throws(() => testAbstractClass.CallBaseFor().AbstractMethod()); - } + [Test] + public void Given_SubstituteForInterface_When_MethodIsCalled_Then_ShouldThrowException() + { + var testInterface = Substitute.For(); + testInterface.TestMethodReturnsInt().Returns(x => x.CallBase()); + Assert.Throws(() => testInterface.TestMethodReturnsInt()); + } - [Test] - public void Given_SubstituteForAction_When_ActionIsCalled_Then_ShouldThrowException() - { - var action = Substitute.For(); - Assert.Throws(() => action.CallBaseFor().Invoke()); - } + [Test] + public void Given_SubstituteForAbstractClass_When_AbstractMethodIsCalled_Then_ShouldThrowException() + { + var testAbstractClass = Substitute.For(); + testAbstractClass.AbstractMethodReturnsSameInt(Arg.Any()).Returns(x => x.CallBase()); + Assert.Throws(() => testAbstractClass.AbstractMethodReturnsSameInt(1)); + } - [Test] - public void Given_SubstituteForFunc_When_FuncIsCalled_Then_ShouldThrowException() - { - var func = Substitute.For>(); - Assert.Throws(() => func.CallBaseFor().Invoke()); - } + [Test] + public void Given_SubstituteForFunc_When_FuncIsCalled_Then_ShouldThrowException() + { + var func = Substitute.For>(); + func().Returns(x => x.CallBase()); + Assert.Throws(() => func()); + } - [Test] - public void Given_SubstituteForEventHandler_When_EventHandlerIsCalled_Then_ShouldThrowException() - { - var eventHandler = Substitute.For(); - Assert.Throws(() => - eventHandler.CallBaseFor().Invoke(Arg.Any(), Arg.Any())); - } + [Test] + public void Given_SubstituteForAbstractClass_When_MethodWithImplementationIsCalled_Then_ShouldCallBaseImplementation() + { + var testAbstractClass = Substitute.For(); + testAbstractClass.MethodReturnsSameInt(Arg.Any()).Returns(x => x.CallBase()); + Assert.That(testAbstractClass.MethodReturnsSameInt(1), Is.EqualTo(1)); + Assert.That(testAbstractClass.CalledTimes, Is.EqualTo(1)); + } - [Test] - public void - Given_SubstituteForAbstractClass_When_MethodWithImplementationIsCalled_Then_ShouldCallBaseImplementation - () - { - var testAbstractClass = Substitute.For(); - testAbstractClass.CallBaseFor().MethodWithImplementation(Arg.Any()); - Assert.That(testAbstractClass.MethodWithImplementation(1), Is.EqualTo(1)); - } + [Test] + [Ignore("Should/can we avoid call base here?")] + public void + Given_SubstituteForClass_When_VirtualMethodsReturnValueIsOverwritten_Then_ShouldNotCallBaseImplementation + () + { + var testClass = Substitute.For(); + testClass.VirtualMethodReturnsSameInt(Arg.Any()).Returns(x => x.CallBase()); + testClass.VirtualMethodReturnsSameInt(Arg.Any()).Returns(1); + Assert.That(testClass.CalledTimes, Is.EqualTo(0)); + } - [Test] - public void Given_SubstituteForClass_When_VirtualMethodIsCalled_Then_ShouldCallBaseImplementation() - { - var testClass = Substitute.For(); - testClass.CallBaseFor().VirtualMethod(); - testClass.VirtualMethod(); - Assert.That(testClass.CalledTimes, Is.EqualTo(1)); - } + [Test] + public void + Given_SubstituteForClass_And_ReturnValueIsOverwritten_When_VirtualMethodIsCalled_Then_ShouldReturnOverwrittenValue + () + { + var testClass = Substitute.For(); + testClass.VirtualMethodReturnsSameInt(Arg.Any()).Returns(x => x.CallBase()); + testClass.VirtualMethodReturnsSameInt(Arg.Any()).Returns(2); + Assert.That(testClass.VirtualMethodReturnsSameInt(1), Is.EqualTo(2)); + } - [Test] - public void Given_SubstituteForClass_When_CallBaseUsed_ShouldNotCallRealImplementation() - { - var testClass = Substitute.For(); - testClass.CallBaseFor().VirtualMethod(); - testClass.CallBaseFor().VirtualMethod(); + [Test] + public void + Given_SubstituteForClass_And_VirtualMethodReturnsFuncs_When_VirtualMethodIsCalled_Then_ShouldReturnCorrectValues + () + { + var testClass = Substitute.For(); + testClass.VirtualMethodReturnsSameInt(Arg.Any()) + .Returns( + x => x.CallBase(), + x => 1, + x => { throw new Exception(); }); + Assert.That(testClass.VirtualMethodReturnsSameInt(2), Is.EqualTo(2)); + Assert.That(testClass.VirtualMethodReturnsSameInt(2), Is.EqualTo(1)); + Assert.Throws(() => testClass.VirtualMethodReturnsSameInt(1)); + } - Assert.That(testClass.CalledTimes, Is.EqualTo(0)); - } - - [Test] - public void Given_SubstituteForClass_When_VirtualMethodIsSetupTwice_Then_ShouldSetupCorrectly() - { - var testClass = Substitute.For(); - testClass.CallBaseFor().VirtualMethod(); - testClass.CallBaseFor().VirtualMethod(); - - testClass.VirtualMethod(); - Assert.That(testClass.CalledTimes, Is.EqualTo(1)); - } - - [Test] - public void Given_SubstituteForClass_When_AbstractMethodIsCalled_Then_ShouldCallBaseImplementation() - { - var testClass = Substitute.For(); - testClass.CallBaseFor().AbstractMethod(); - testClass.AbstractMethod(); - Assert.That(testClass.CalledTimes, Is.EqualTo(1)); - } - - [Test] - public void Given_SubstituteForClass_When_VirtualMethodIsCalledTwice_Then_ShouldCallBaseImplementationTwice() - { - var testClass = Substitute.For(); - 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.CallBaseFor().VirtualMethodReturnsSameInt(Arg.Any()); - testClass.VirtualMethodReturnsSameInt(Arg.Any()).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.CallBaseFor().VirtualMethodReturnsSameInt(Arg.Any()); - testClass.VirtualMethodReturnsSameInt(Arg.Any()).Returns(2); - Assert.That(testClass.VirtualMethodReturnsSameInt(1), Is.EqualTo(2)); - } - - [Test] - public void - Given_SubstituteForClass_And_ObjectReturnValueIsOverwritten_When_VirtualMethodIsCalled_Then_ShouldReturnOverwrittenValue - () - { - var testClass = Substitute.For(); - testClass.CallBaseFor().VirtualMethodReturnsSameObject(Arg.Any()); - var objectToReturn = new object(); - testClass.VirtualMethodReturnsSameObject(Arg.Any()).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(); - 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(); - 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.CallBaseFor().VirtualMethodReturnsSameInt(Arg.Any()); - 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.CallBaseFor().VirtualMethodReturnsSameInt(Arg.Any()); - testClass.VirtualMethodReturnsSameInt(Arg.Is(1)).Returns(2); - Assert.That(testClass.VirtualMethodReturnsSameInt(3), Is.EqualTo(3)); - Assert.That(testClass.CalledTimes, Is.EqualTo(1)); + [Test] + public void + Given_SubstituteForClass_And_VirtualMethodReturnsFuncs_When_VirtualMethodIsCalled_Then_ShouldCallBaseCorrect + () + { + var testClass = Substitute.For(); + testClass.VirtualMethodReturnsSameInt(Arg.Any()) + .Returns( + x => x.CallBase(), + x => 1, + x => 2); + testClass.VirtualMethodReturnsSameInt(1); + testClass.VirtualMethodReturnsSameInt(1); + testClass.VirtualMethodReturnsSameInt(1); + Assert.That(testClass.CalledTimes, Is.EqualTo(1)); + } } public interface ITestInterface { - void TestMethod(); + void VoidTestMethod(); + int TestMethodReturnsInt(); } public abstract class TestAbstractClass { - public abstract void AbstractMethod(); + public int CalledTimes { get; set; } - public virtual int MethodWithImplementation(int i) + public abstract void VoidAbstractMethod(); + + public abstract int AbstractMethodReturnsSameInt(int i); + + public virtual int MethodReturnsSameInt(int i) { + CalledTimes++; return i; } } public class TestClass : TestAbstractClass { - public int CalledTimes { get; set; } public virtual int VirtualMethodReturnsSameInt(int i) { @@ -217,15 +241,21 @@ namespace NSubstitute.Acceptance.Specs return o; } - public virtual void VirtualMethod() + public virtual void VoidVirtualMethod() { CalledTimes++; } - public override void AbstractMethod() + public override void VoidAbstractMethod() { CalledTimes++; } + + public override int AbstractMethodReturnsSameInt(int i) + { + CalledTimes++; + return i; + } } } } \ No newline at end of file diff --git a/Source/NSubstitute/NSubstitute.csproj b/Source/NSubstitute/NSubstitute.csproj index 19a6aba..4246a0b 100644 --- a/Source/NSubstitute/NSubstitute.csproj +++ b/Source/NSubstitute/NSubstitute.csproj @@ -189,7 +189,6 @@ - diff --git a/Source/NSubstitute/Proxies/CastleDynamicProxy/CastleInvocationMapper.cs b/Source/NSubstitute/Proxies/CastleDynamicProxy/CastleInvocationMapper.cs index 37f0a60..e1c903e 100644 --- a/Source/NSubstitute/Proxies/CastleDynamicProxy/CastleInvocationMapper.cs +++ b/Source/NSubstitute/Proxies/CastleDynamicProxy/CastleInvocationMapper.cs @@ -10,22 +10,18 @@ namespace NSubstitute.Proxies.CastleDynamicProxy public virtual ICall Map(IInvocation castleInvocation) { - //Func baseMethod = null; - //if (castleInvocation.InvocationTarget != null && - // castleInvocation.MethodInvocationTarget.IsAbstract != true) - //{ - // baseMethod = () => - // { - // castleInvocation.Proceed(); - // return castleInvocation.ReturnValue; - // }; - //} - - Func baseMethod = () => + Func baseMethod = null; + if (castleInvocation.InvocationTarget != null && + castleInvocation.MethodInvocationTarget.IsVirtual && + !castleInvocation.MethodInvocationTarget.IsAbstract && + !castleInvocation.MethodInvocationTarget.IsFinal) + { + baseMethod = () => { castleInvocation.Proceed(); return castleInvocation.ReturnValue; }; + } return CallFactory.Create(castleInvocation.Method, castleInvocation.Arguments, castleInvocation.Proxy, baseMethod); } diff --git a/Source/NSubstitute/Routing/Handlers/SetBaseForCallHandler.cs b/Source/NSubstitute/Routing/Handlers/SetBaseForCallHandler.cs deleted file mode 100644 index a9ffa6b..0000000 --- a/Source/NSubstitute/Routing/Handlers/SetBaseForCallHandler.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Reflection; -using NSubstitute.Core; -using NSubstitute.Exceptions; - -namespace NSubstitute.Routing.Handlers -{ - public class SetBaseForCallHandler : ICallHandler - { - private readonly ICallSpecificationFactory _callSpecificationFactory; - private readonly ICallResults _callResults; - private readonly MatchArgs _matchArgs; - - public SetBaseForCallHandler(ICallSpecificationFactory callSpecificationFactory, ICallResults callResults, MatchArgs matchArgs) - { - _callSpecificationFactory = callSpecificationFactory; - _callResults = callResults; - _matchArgs = matchArgs; - } - - public RouteAction Handle(ICall call) - { - var method = call.GetMethodInfo(); - if (!CanCallBase(method)) - { - throw new CouldNotCallBaseException(method); - } - var callSpec = _callSpecificationFactory.CreateFrom(call, _matchArgs); - _callResults.SetResult(callSpec, new ReturnValueFromBase(method.ReturnType)); - return RouteAction.Continue(); - } - - private bool CanCallBase(MethodInfo methodInfo) - { - return methodInfo.IsVirtual && !methodInfo.IsFinal && !methodInfo.IsAbstract; - } - - class ReturnValueFromBase : IReturn - { - private readonly Type _expectedType; - public ReturnValueFromBase(Type expectedType) { _expectedType = expectedType; } - public object ReturnFor(CallInfo info) { return info.CallBase(); } - public Type TypeOrNull() { return _expectedType; } - public bool CanBeAssignedTo(Type t) { return _expectedType.IsAssignableFrom(t); } - } - } -} \ No newline at end of file diff --git a/Source/NSubstitute/Routing/IRouteFactory.cs b/Source/NSubstitute/Routing/IRouteFactory.cs index 35a8f9a..c3a5950 100644 --- a/Source/NSubstitute/Routing/IRouteFactory.cs +++ b/Source/NSubstitute/Routing/IRouteFactory.cs @@ -5,7 +5,6 @@ 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 doAction, MatchArgs matchArgs); diff --git a/Source/NSubstitute/Routing/RouteFactory.cs b/Source/NSubstitute/Routing/RouteFactory.cs index 84ec689..2f586d8 100644 --- a/Source/NSubstitute/Routing/RouteFactory.cs +++ b/Source/NSubstitute/Routing/RouteFactory.cs @@ -6,15 +6,6 @@ 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.CallResults, matchArgs) - , ReturnDefaultForReturnTypeHandler() - }); - } public IRoute CallQuery(ISubstituteState state) { return new Route(new ICallHandler[] { diff --git a/Source/NSubstitute/SubstituteExtensions.cs b/Source/NSubstitute/SubstituteExtensions.cs index 695985c..990810a 100644 --- a/Source/NSubstitute/SubstituteExtensions.cs +++ b/Source/NSubstitute/SubstituteExtensions.cs @@ -224,26 +224,6 @@ namespace NSubstitute return GetRouterForSubstitute(substitute).ReceivedCalls(); } - /// - /// Calls base implementation for the specified member called with any args. - /// - public static T CallBaseWithAnyArgsFor(this T substitute) where T : class - { - var router = GetRouterForSubstitute(substitute); - router.SetRoute(x => RouteFactory().CallBase(x, MatchArgs.Any)); - return substitute; - } - - /// - /// Calls base implementation for the specified member. - /// - public static T CallBaseFor(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 substitute) { var context = SubstitutionContext.Current;