Skip to content

Commit

Permalink
Add MockCapture
Browse files Browse the repository at this point in the history
  • Loading branch information
ocoanet committed Jun 20, 2016
1 parent 551aa86 commit b7817cf
Show file tree
Hide file tree
Showing 6 changed files with 414 additions and 0 deletions.
97 changes: 97 additions & 0 deletions Source/Capture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//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.Expressions;

namespace Moq
{
/// <summary>
/// Allows to create parameter captures in setup expressions.
/// </summary>
public static class Capture
{
/// <summary>
/// Creates a parameter capture that will store items in a collection.
/// </summary>
/// <typeparam name="T">The captured object item</typeparam>
/// <param name="collection">The collection that will store captured parameter values</param>
/// <example>
/// Arrange code:
/// <code>
/// var parameters = new List{string}();
/// mock.Setup(x => x.DoSomething(Capture.In(parameters)));
/// </code>
/// Assert code:
/// <code>
/// Assert.Equal("Hello!", parameters.Single());
/// </code>
/// </example>
public static T In<T>(IList<T> collection)
{
var capture = new MockCapture<T>(collection);
return capture.CaptureAny();
}

/// <summary>
/// Creates a parameter capture that will store specific items in a collection.
/// </summary>
/// <typeparam name="T">The captured object item</typeparam>
/// <param name="collection">The collection that will store captured parameter values</param>
/// <param name="match">A predicate used to filter captured parameters</param>
/// <example>
/// Arrange code:
/// <code>
/// var parameters = new List{string}();
/// mock.Setup(x => x.DoSomething(Capture.In(parameters, p => p.StartsWith("W"))));
/// </code>
/// Assert code:
/// <code>
/// Assert.Equal("Hello!", parameters.Single());
/// </code>
/// </example>
public static T In<T>(IList<T> collection, Expression<Func<T, bool>> match)
{
var capture = new MockCapture<T>(collection);
return capture.Capture(match);
}
}
}
148 changes: 148 additions & 0 deletions Source/MockCapture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
//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.Collections.ObjectModel;
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 static readonly Func<T, T> identity = _ => _;
private readonly IList<T> capturedValues = new List<T>();
private readonly Func<T, T> captureCallback;

/// <summary>
/// Initializes an instance of the capture.
/// </summary>
public MockCapture()
: this(identity)
{
}

/// <summary>
/// Initializes an instance of the capture.
/// </summary>
/// <param name="captureCallback">An action to run on captured value</param>
public MockCapture(Action<T> captureCallback)
: this(CreateCaptureCallback(captureCallback))
{
}

/// <summary>
/// Initializes an instance of the capture.
/// </summary>
/// <param name="captureCallback">A transform function to apply to captured values</param>
public MockCapture(Func<T, T> captureCallback)
{
this.captureCallback = captureCallback;
this.capturedValues = new List<T>();
this.Values = new ReadOnlyCollection<T>(this.capturedValues);
}

internal MockCapture(IList<T> capturedValues)
{
this.captureCallback = identity;
this.capturedValues = capturedValues;
this.Values = new ReadOnlyCollection<T>(this.capturedValues);
}

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

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

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

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

private bool EvaluateParameter(T item)
{
var capturedValue = this.captureCallback.Invoke(item);
this.capturedValues.Add(capturedValue);

return true;
}

private static Func<T, T> CreateCaptureCallback(Action<T> action)
{
return x =>
{
action.Invoke(x);
return x;
};
}
}
}
2 changes: 2 additions & 0 deletions Source/Moq.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Capture.cs" />
<Compile Include="ConditionalContext.cs" />
<Compile Include="IInterceptStrategy.cs" />
<Compile Include="IMock.cs" />
Expand All @@ -64,6 +65,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
40 changes: 40 additions & 0 deletions UnitTests/CaptureFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Collections.Generic;
using Xunit;

namespace Moq.Tests
{
public class CaptureFixture
{
[Fact]
public void CanCaptureAnyParameterInCollection()
{
var items = new List<string>();
var mock = new Mock<IFoo>();
mock.Setup(x => x.DoSomething(Capture.In(items)));

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

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

[Fact]
public void CanCaptureSpecificParameterInCollection()
{
var items = new List<string>();
var mock = new Mock<IFoo>();
mock.Setup(x => x.DoSomething(Capture.In(items, p => p.StartsWith("W"))));

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

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

public interface IFoo
{
void DoSomething(string s);
}
}
}
Loading

0 comments on commit b7817cf

Please sign in to comment.