Skip to content

Commit

Permalink
Add support for void sequences (#463)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbestul authored and stakx committed Oct 2, 2017
1 parent 03bbf8b commit 48c6aa6
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 0 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).


## Unreleased

#### Added

* Support for sequential setup of `void` methods (@alexbestul, #463)


## 4.7.137 (2017-09-30)

#### Changed
Expand Down
94 changes: 94 additions & 0 deletions Source/Language/ISetupSequentialAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//Copyright (c) 2007. Clarius Consulting, Manas Technology Solutions, InSTEDD
//https://github.com/moq/moq4
//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;

namespace Moq.Language
{
/// <summary>
/// Defines the <c>Pass</c> and <c>Throws</c> verbs for sequence setups
/// on <c>void</c> methods.
/// </summary>
public interface ISetupSequentialAction
{
/// <summary>
/// Configures the next call in the sequence to do nothing.
/// </summary>
/// <example>
/// The following code configures the first call to <c>Execute()</c>
/// to do nothing, and the second call to throw an exception.
/// <code>
/// mock.SetupSequence(m => m.Execute())
/// .Pass()
/// .Throws&lt;InvalidOperationException&gt;();
/// </code>
/// </example>
ISetupSequentialAction Pass();

/// <summary>
/// Configures the next call in the sequence to throw an exception.
/// </summary>
/// <example>
/// The following code configures the first call to <c>Execute()</c>
/// to do nothing, and the second call to throw an exception.
/// <code>
/// mock.SetupSequence(m => m.Execute())
/// .Pass()
/// .Throws&lt;InvalidOperationException&gt;();
/// </code>
/// </example>
ISetupSequentialAction Throws<TException>()
where TException : Exception, new();

/// <summary>
/// Configures the next call in the sequence to throw an exception.
/// </summary>
/// <example>
/// The following code configures the first call to <c>Execute()</c>
/// to do nothing, and the second call to throw an exception.
/// <code>
/// mock.SetupSequence(m => m.Execute())
/// .Pass()
/// .Throws(new InvalidOperationException());
/// </code>
/// </example>
ISetupSequentialAction Throws(Exception exception);
}
}
11 changes: 11 additions & 0 deletions Source/SequenceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ public static class SequenceExtensions
return new SetupSequentialContext<TMock, TResult>(mock, expression);
}

/// <summary>
/// Performs a sequence of actions, one per call.
/// </summary>
public static ISetupSequentialAction SetupSequence<TMock>(
this Mock<TMock> mock,
Expression<Action<TMock>> expression)
where TMock : class
{
return new SetupSequentialActionContext<TMock>(mock, expression);
}

/// <summary>
/// Return a sequence of tasks, once per call.
/// </summary>
Expand Down
107 changes: 107 additions & 0 deletions Source/SetupSequentialActionContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//Copyright (c) 2007. Clarius Consulting, Manas Technology Solutions, InSTEDD
//https://github.com/moq/moq4
//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.Linq.Expressions;
using Moq.Language;
using Moq.Language.Flow;

namespace Moq
{
internal sealed class SetupSequentialActionContext<TMock> : ISetupSequentialAction
where TMock : class
{
private int currentStep;
private int expectationsCount;
private Mock<TMock> mock;
private Expression<Action<TMock>> expression;
private readonly Action callbackAction;

public SetupSequentialActionContext(
Mock<TMock> mock,
Expression<Action<TMock>> expression)
{
this.mock = mock;
this.expression = expression;
this.callbackAction = () => currentStep++;
}

public ISetupSequentialAction Pass()
{
var setup = this.GetSetup();
setup.Callback(DoNothing);
this.EndSetup(setup);
return this;
}

private static void DoNothing() { }

public ISetupSequentialAction Throws<TException>() where TException : Exception, new()
{
var setup = this.GetSetup();
setup.Throws<TException>();
this.EndSetup(setup);
return this;
}

public ISetupSequentialAction Throws(Exception exception)
{
var setup = this.GetSetup();
setup.Throws(exception);
this.EndSetup(setup);
return this;
}

private void EndSetup(ICallback callback)
{
callback.Callback(callbackAction);
}

private ISetup<TMock> GetSetup()
{
var expectationStep = this.expectationsCount;
this.expectationsCount++;

return this.mock
.When(() => currentStep == expectationStep)
.Setup(expression);
}
}
}
83 changes: 83 additions & 0 deletions UnitTests/SequentialActionExtensionsFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//Copyright (c) 2007. Clarius Consulting, Manas Technology Solutions, InSTEDD
//https://github.com/moq/moq4
//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 Xunit;

namespace Moq.Tests
{
public class SequentialActionExtensionsFixture
{
[Fact]
public void PerformSequence()
{
var mock = new Mock<IFoo>();

mock.SetupSequence(m => m.Do())
.Pass()
.Throws<InvalidOperationException>()
.Throws(new ArgumentException());

mock.Object.Do();
Assert.Throws<InvalidOperationException>(() => mock.Object.Do());
Assert.Throws<ArgumentException>(() => mock.Object.Do());
}

[Fact]
public void PerformSequenceWithThrowFirst()
{
var mock = new Mock<IFoo>();

mock.SetupSequence(m => m.Do())
.Throws<InvalidOperationException>()
.Pass()
.Throws(new ArgumentException());

Assert.Throws<InvalidOperationException>(() => mock.Object.Do());
mock.Object.Do();
Assert.Throws<ArgumentException>(() => mock.Object.Do());
}

public interface IFoo
{
void Do();
}
}
}

0 comments on commit 48c6aa6

Please sign in to comment.