From fa44a0baa229f72fa0e221048aceb48560155453 Mon Sep 17 00:00:00 2001 From: Olivier Coanet Date: Wed, 9 Mar 2016 09:30:21 +0100 Subject: [PATCH] Add MockCapture --- Source/MockCapture.cs | 139 +++++++++++++++++++++++++++ Source/Moq.csproj | 1 + UnitTests/MockCaptureFixture.cs | 163 ++++++++++++++++++++++++++++++++ UnitTests/Moq.Tests.csproj | 3 +- 4 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 Source/MockCapture.cs create mode 100644 UnitTests/MockCaptureFixture.cs diff --git a/Source/MockCapture.cs b/Source/MockCapture.cs new file mode 100644 index 000000000..d1f9e4f4b --- /dev/null +++ b/Source/MockCapture.cs @@ -0,0 +1,139 @@ +//Copyright (c) 2007. Clarius Consulting, Manas Technology Solutions, InSTEDD +//http://code.google.com/p/moq/ +//All rights reserved. + +//Redistribution and use in source and binary forms, +//with or without modification, are permitted provided +//that the following conditions are met: + +// * Redistributions of source code must retain the +// above copyright notice, this list of conditions and +// the following disclaimer. + +// * Redistributions in binary form must reproduce +// the above copyright notice, this list of conditions +// and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// * Neither the name of Clarius Consulting, Manas Technology Solutions or InSTEDD nor the +// names of its contributors may be used to endorse +// or promote products derived from this software +// without specific prior written permission. + +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +//CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +//INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +//MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +//DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +//SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +//WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +//OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. + +//[This is the BSD license, see +// http://www.opensource.org/licenses/bsd-license.php] + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Moq +{ + /// + /// Captures mock parameters. + /// + /// + /// Arrange code: + /// + /// var capture = new MockCapture{string}(); + /// mock.Setup(x => x.DoSomething(capture.CaptureAny())); + /// + /// Assert code: + /// + /// Assert.Equal("Hello!", capture.Last); + /// + /// + public class MockCapture + { + private readonly List capturedValues = new List(); + private Action callback; + private Func copier; + + /// + /// Initializes an instance of the capture. + /// + public MockCapture() + { + this.All = this.capturedValues.AsReadOnly(); + } + + /// + /// Gets all the captured parameter values. + /// + public IReadOnlyList All { get; private set; } + + /// + /// Gets the last captured parameter value. + /// + public T Last { get { return this.All.Last(); } } + + /// + /// Gets the single captured parameter value. + /// + public T Single { get { return this.All.Single(); } } + + /// + /// Gets the parameter to use in the setup expression. + /// + /// The setup expression parameter + public T CaptureAny() + { + return Match.Create(value => this.CaptureParameter(value), () => It.IsAny()); + } + + /// + /// Gets the parameter to use in the setup expression. + /// + /// The setup expression parameter + public T Capture(Expression> match) + { + var matchDelegate = match.Compile(); + return Match.Create(value => matchDelegate.Invoke(value) && this.CaptureParameter(value), () => It.Is(match)); + } + + /// + /// Copy captured parameters with the specified delegate. + /// + /// The delegate used to clone parameters + public void CopyOnCapture(Func copier) + { + this.copier = copier; + } + + /// + /// Provide an action to run on captured parameters. + /// + /// an action to run on the captured parameters + public void RunOnCapture(Action callback) + { + this.callback = callback; + } + + private bool CaptureParameter(T item) + { + var capturedValue = this.copier != null ? this.copier.Invoke(item) : item; + this.capturedValues.Add(capturedValue); + + if (this.callback != null) + this.callback.Invoke(item); + + return true; + } + } +} \ No newline at end of file diff --git a/Source/Moq.csproj b/Source/Moq.csproj index 4e72af310..855cdb87b 100644 --- a/Source/Moq.csproj +++ b/Source/Moq.csproj @@ -64,6 +64,7 @@ True IReturns.tt + diff --git a/UnitTests/MockCaptureFixture.cs b/UnitTests/MockCaptureFixture.cs new file mode 100644 index 000000000..bd76dfa6e --- /dev/null +++ b/UnitTests/MockCaptureFixture.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using Xunit; + +namespace Moq.Tests +{ + public class MockCaptureFixture + { + [Fact] + public void CanCaptureParameter() + { + var capture = new MockCapture(); + var mock = new Mock(); + mock.Setup(x => x.DoSomething(capture.CaptureAny())); + + mock.Object.DoSomething("Hello!"); + + var expectedValues = new List { "Hello!" }.AsReadOnly(); + Assert.Equal(expectedValues, capture.All); + } + + [Fact] + public void CanCaptureMultipleParameters() + { + var capture = new MockCapture(); + var mock = new Mock(); + mock.Setup(x => x.DoSomething(capture.CaptureAny())); + + mock.Object.DoSomething("Hello!"); + mock.Object.DoSomething("World!"); + + var expectedValues = new List { "Hello!", "World!" }.AsReadOnly(); + Assert.Equal(expectedValues, capture.All); + } + + [Fact] + public void CanCaptureSpecificParameter() + { + var capture = new MockCapture(); + var mock = new Mock(); + mock.Setup(x => x.DoSomething(capture.Capture(p => p.StartsWith("W")))); + + mock.Object.DoSomething("Hello!"); + mock.Object.DoSomething("World!"); + + var expectedValues = new List { "World!" }.AsReadOnly(); + Assert.Equal(expectedValues, capture.All); + } + + [Fact] + public void CanCaptureLastParameter() + { + var capture = new MockCapture(); + var mock = new Mock(); + mock.Setup(x => x.DoSomething(capture.CaptureAny())); + + mock.Object.DoSomething("Hello!"); + mock.Object.DoSomething("World!"); + + Assert.Equal("World!", capture.Last); + } + + [Fact] + public void CanCaptureSingleParameter() + { + var capture = new MockCapture(); + var mock = new Mock(); + mock.Setup(x => x.DoSomething(capture.CaptureAny())); + + mock.Object.DoSomething("Hello!"); + + Assert.Equal("Hello!", capture.Single); + } + + [Fact] + public void ThrowsIfSingleIsCalledWithNoParameter() + { + var capture = new MockCapture(); + var mock = new Mock(); + mock.Setup(x => x.DoSomething(capture.CaptureAny())); + + Assert.Throws(() => capture.Single); + } + + [Fact] + public void ThrowsIfSingleIsCalledWithManyParameter() + { + var capture = new MockCapture(); + var mock = new Mock(); + mock.Setup(x => x.DoSomething(capture.CaptureAny())); + + mock.Object.DoSomething("Hello!"); + mock.Object.DoSomething("World!"); + + Assert.Throws(() => capture.Single); + } + + [Fact] + public void CanRunCallbackOnParameter() + { + var capture = new MockCapture(); + capture.RunOnCapture(x => x.Value = 42); + + var mock = new Mock(); + mock.Setup(x => x.DoSomething(capture.CaptureAny())); + + var param = new FooParam(0); + mock.Object.DoSomething(param); + + Assert.Equal(42, param.Value); + } + + [Fact] + public void CanCopyParameter() + { + var capture = new MockCapture(); + capture.CopyOnCapture(x => new FooParam(x.Value)); + + var mock = new Mock(); + mock.Setup(x => x.DoSomething(capture.CaptureAny())); + + var param = new FooParam(41); + mock.Object.DoSomething(param); + param.Value = 42; + mock.Object.DoSomething(param); + param.Value = 0; + + var expectedValues = new List { new FooParam(41), new FooParam(42) }.AsReadOnly(); + Assert.Equal(expectedValues, capture.All); + } + + public interface IFoo + { + void DoSomething(string item); + void DoSomething(FooParam item); + } + + public class FooParam : IEquatable + { + public FooParam(int value) + { + Value = value; + } + + public int Value { get; set; } + + public bool Equals(FooParam other) + { + return other != null && Value == other.Value; + } + + public override bool Equals(object obj) + { + return Equals(obj as FooParam); + } + + public override int GetHashCode() + { + return Value; + } + } + } +} \ No newline at end of file diff --git a/UnitTests/Moq.Tests.csproj b/UnitTests/Moq.Tests.csproj index 4356c4210..aff992b51 100644 --- a/UnitTests/Moq.Tests.csproj +++ b/UnitTests/Moq.Tests.csproj @@ -58,6 +58,7 @@ + @@ -110,4 +111,4 @@ - \ No newline at end of file +