Skip to content

Commit

Permalink
Add MockCapture
Browse files Browse the repository at this point in the history
  • Loading branch information
ocoanet committed Jun 16, 2016
1 parent 551aa86 commit a00ddf9
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 1 deletion.
139 changes: 139 additions & 0 deletions Source/MockCapture.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Captures mock parameters.
/// </summary>
/// <example>
/// Arrange code:
/// <code>
/// var capture = new MockCapture{string}();
/// mock.Setup(x => x.DoSomething(capture.CaptureAny()));
/// </code>
/// Assert code:
/// <code>
/// Assert.Equal("Hello!", capture.Last);
/// </code>
/// </example>
public class MockCapture<T>
{
private readonly List<T> _capturedValues = new List<T>();
private Action<T> _callback;
private Func<T, T> _copier;

/// <summary>
/// Initializes an instance of the capture.
/// </summary>
public MockCapture()
{
All = _capturedValues.AsReadOnly();
}

/// <summary>
/// Gets all the captured parameter values.
/// </summary>
public IReadOnlyList<T> All { get; private set; }

/// <summary>
/// Gets the last captured parameter value.
/// </summary>
public T Last { get { return All.Last(); } }

/// <summary>
/// Gets the single captured parameter value.
/// </summary>
public T Single { get { return All.Single(); } }

/// <summary>
/// Gets the parameter to use in the setup expression.
/// </summary>
/// <returns>The setup expression parameter</returns>
public T CaptureAny()
{
return Match.Create(value => CaptureParameter(value), () => It.IsAny<T>());
}

/// <summary>
/// Gets the parameter to use in the setup expression.
/// </summary>
/// <returns>The setup expression parameter</returns>
public T Capture(Expression<Func<T, bool>> match)
{
var matchDelegate = match.Compile();
return Match.Create(value => matchDelegate.Invoke(value) && CaptureParameter(value), () => It.Is<T>(match));
}

/// <summary>
/// Copy captured parameters with the specified delegate.
/// </summary>
/// <param name="copier">The delegate used to clone parameters</param>
public void CopyOnCapture(Func<T, T> copier)
{
_copier = copier;
}

/// <summary>
/// Provide an action to run on captured parameters.
/// </summary>
/// <paramref name="callback">an action to run on the captured parameters</paramref>
public void RunOnCapture(Action<T> callback)
{
_callback = callback;
}

private bool CaptureParameter(T item)
{
var capturedValue = _copier != null ? _copier.Invoke(item) : item;
_capturedValues.Add(capturedValue);

if (_callback != null)
_callback.Invoke(item);

return true;
}
}
}
1 change: 1 addition & 0 deletions Source/Moq.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>IReturns.tt</DependentUpon>
</Compile>
<Compile Include="MockCapture.cs" />
<Compile Include="Proxy\ProxyGenerationHelpers.cs" />
<Compile Include="ReturnsExtensions.cs" />
<Compile Include="Language\ISetupSequentialResult.cs" />
Expand Down
163 changes: 163 additions & 0 deletions UnitTests/MockCaptureFixture.cs
Original file line number Diff line number Diff line change
@@ -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<string>();
var mock = new Mock<IFoo>();
mock.Setup(x => x.DoSomething(capture.CaptureAny()));

mock.Object.DoSomething("Hello!");

var expectedValues = new List<string> { "Hello!" }.AsReadOnly();
Assert.Equal(expectedValues, capture.All);
}

[Fact]
public void CanCaptureMultipleParameters()
{
var capture = new MockCapture<string>();
var mock = new Mock<IFoo>();
mock.Setup(x => x.DoSomething(capture.CaptureAny()));

mock.Object.DoSomething("Hello!");
mock.Object.DoSomething("World!");

var expectedValues = new List<string> { "Hello!", "World!" }.AsReadOnly();
Assert.Equal(expectedValues, capture.All);
}

[Fact]
public void CanCaptureSpecificParameter()
{
var capture = new MockCapture<string>();
var mock = new Mock<IFoo>();
mock.Setup(x => x.DoSomething(capture.Capture(p => p.StartsWith("W"))));

mock.Object.DoSomething("Hello!");
mock.Object.DoSomething("World!");

var expectedValues = new List<string> { "World!" }.AsReadOnly();
Assert.Equal(expectedValues, capture.All);
}

[Fact]
public void CanCaptureLastParameter()
{
var capture = new MockCapture<string>();
var mock = new Mock<IFoo>();
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<string>();
var mock = new Mock<IFoo>();
mock.Setup(x => x.DoSomething(capture.CaptureAny()));

mock.Object.DoSomething("Hello!");

Assert.Equal("Hello!", capture.Single);
}

[Fact]
public void ThrowsIfSingleIsCalledWithNoParameter()
{
var capture = new MockCapture<string>();
var mock = new Mock<IFoo>();
mock.Setup(x => x.DoSomething(capture.CaptureAny()));

Assert.Throws<InvalidOperationException>(() => capture.Single);
}

[Fact]
public void ThrowsIfSingleIsCalledWithManyParameter()
{
var capture = new MockCapture<string>();
var mock = new Mock<IFoo>();
mock.Setup(x => x.DoSomething(capture.CaptureAny()));

mock.Object.DoSomething("Hello!");
mock.Object.DoSomething("World!");

Assert.Throws<InvalidOperationException>(() => capture.Single);
}

[Fact]
public void CanRunCallbackOnParameter()
{
var capture = new MockCapture<FooParam>();
capture.RunOnCapture(x => x.Value = 42);

var mock = new Mock<IFoo>();
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<FooParam>();
capture.CopyOnCapture(x => new FooParam(x.Value));

var mock = new Mock<IFoo>();
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<FooParam> { 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<FooParam>
{
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;
}
}
}
}
3 changes: 2 additions & 1 deletion UnitTests/Moq.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<Compile Include="ConditionalSetupFixture.cs" />
<Compile Include="CustomMatcherFixture.cs" />
<Compile Include="ExtensionsFixture.cs" />
<Compile Include="MockCaptureFixture.cs" />
<Compile Include="ReturnsExtensionsFixture.cs" />
<Compile Include="Linq\MockRepositoryQuerying.cs" />
<Compile Include="MockedDelegatesFixture.cs" />
Expand Down Expand Up @@ -110,4 +111,4 @@
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>

0 comments on commit a00ddf9

Please sign in to comment.