Introduction to nsubstitute
-
Upload
suresh-loganatha -
Category
Technology
-
view
249 -
download
4
Transcript of Introduction to nsubstitute
![Page 1: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/1.jpg)
Introduction to
NSubstitute
![Page 2: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/2.jpg)
Agenda
Test double and its Types
Test Driven Development
Introduction to NSubstitute
Getting started
Creating a substitute & Setting a return value
Return for specific args, return for any args & return from a
function
Multiple return values
Replacing return values
Checking received calls & Clearing received calls
Argument matchers
Void calls and When..Do
![Page 3: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/3.jpg)
Cont..
Throwing exceptions
Raising events
Auto and recursive mocks
Setting out and ref args
Checking call order
Partial subs
Advantage of NSubstitute
Alternatives
Q & A
![Page 4: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/4.jpg)
Test double and its types
“Test Double” is a generic term for any case where you replace
a production object for testing purposes.
Test double types as listed below,
◦ Stub (used for providing the tested code with "indirect input")
◦ Mock (used for verifying "indirect output" of the tested code,
by first defining the expectations before the tested code is
executed)
◦ Spies (Are stubs that also record some information based on
how they were called. One form of this might be an email
service that records how many messages it was sent)
◦ Fake object (used as a simpler implementation, e.g. using an
in-memory database in the tests instead of doing real
database access)
◦ Dummy object (used when a parameter is needed for the
tested method but without actually needing to use the
parameter)
![Page 5: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/5.jpg)
Test Driven Development
TDD is an evolutionary approach to software development,
where you write a test cases first then you write just enough
production code to fulfill that test cases to pass and then
refactor the code.
The result of using this practice is a comprehensive suite of
unit tests that can be run at any time to provide feedback that
the software is still working.
![Page 6: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/6.jpg)
Introduction to NSubstitute
NSubstitute is a friendly substitute for .NET mocking
frameworks. It has a simple, succinct syntax to help
developers write clearer tests.
NSubstitute is designed for Arrange-Act-Assert (AAA) testing
and with Test Driven Development (TDD) in mind.
It’s a open source framework.
Latest version is 1.8.1.
![Page 7: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/7.jpg)
Getting started
The easiest way to get started is to reference NSubstitute
from your test project using the Nsubstitute NuGet package
via NuGet.
Alternatively you can download NSubstitute and add a
reference to the NSubstitute.dll file included in the download
into your test project.
![Page 8: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/8.jpg)
Creating a substitute & Setting a
return value The basic syntax for creating a substitute is:
var logSub = Substitute.For<ISomeInterface>();
Warning: Substituting for classes can have some nasty side-
effects. For starters, NSubstitute can only work with virtual
members of the class, so any non-virtual code in the class will
actually execute! If possible, stick to substituting interfaces.
Return value can be set for a method or property.
var calculator = Substitute.For<ICalculator>();
calculator.Add(1, 2).Returns(3);
calculator.Mode.Returns("DEC");
![Page 9: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/9.jpg)
Return for specific args, Return
for any args & Return from a
function Return for specific args
Return values can be configured for different combinations of
arguments passed to calls using argument matchers.
//Return when first arg is anything and second arg is 5:
calculator.Add(Arg.Any<int>(), 5).Returns(10);
Assert.AreEqual(10, calculator.Add(123, 5));
![Page 10: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/10.jpg)
Return for any args
A call can be configured to return a value regardless of the
arguments passed using the ReturnsForAnyArgs() extension
method.
//Return 100 regardless for the argument passed:
calculator.Add(1, 2).ReturnsForAnyArgs(100);
Assert.AreEqual(calculator.Add(1, 2), 100);
Assert.AreEqual(calculator.Add(-7, 15), 100);
![Page 11: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/11.jpg)
Return from a function
The return value for a call to a property or method can be set to
the result of a function. This allows more complex logic to be put
into the substitute. Although this is normally a bad practice, there
are some situations in which it is useful.
public interface IFoo {
string Bar(int a, string b);
}
var foo = Substitute.For<IFoo>();
foo.Bar(0, "").ReturnsForAnyArgs(x => "Hello " + x.Arg<string>());
Assert.That(foo.Bar(1, "World"), Is.EqualTo("Hello World"));
![Page 12: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/12.jpg)
Multiple return values
A call can also be configured to return a different value over
multiple calls.
var calculator = Substitute.For<ICalculator>();
calculator.Mode.Returns("DEC", "HEX", "BIN");
Assert.AreEqual("DEC", calculator.Mode);
Assert.AreEqual("HEX", calculator.Mode);
Assert.AreEqual("BIN", calculator.Mode);
![Page 13: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/13.jpg)
Replacing return values
The return value for a method or property can be set as many
times as required. Only the most recently set value will be
returned.
calculator.Mode.Returns("DEC,HEX,OCT");
calculator.Mode.Returns(x => "???");
calculator.Mode.Returns("HEX");
calculator.Mode.Returns("BIN");
Assert.AreEqual(calculator.Mode, "BIN");
![Page 14: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/14.jpg)
Checking received calls &
Clearing received calls Checking received calls
In some cases (particularly for void methods) it is useful to check that a specific call has been received by a substitute. This can be checked using the Received() extension method, followed by the call being checked. There is DidNotReceive() method to check if call is not recieved.
public class SomethingThatNeedsACommand
{
ICommand command;
public SomethingThatNeedsACommand(ICommand command) {
this.command = command; }
public void DoSomething() { command.Execute(); }
public void DontDoAnything() { }
}
![Page 15: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/15.jpg)
[TestMethod]
public void Should_execute_command()
{
//Arrange
var command = Substitute.For<ICommand>();
var something = newSomethingThatNeedsACommand(command);
//Act
something.DoSomething();
//Assert
command.Received().Execute();
}
Clearing received calls
A substitute can forget all the calls previously made to it using
the ClearReceivedCalls() extension method.
![Page 16: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/16.jpg)
Argument matchers
Argument matchers can be used when setting return values
and when checking received calls. They provide a way to
specify a call or group of calls, so that a return value can be
set for all matching calls, or to check a matching call has
been receieved.
Ignoring arguments
calculator.Add(Arg.Any<int>(), 5).Returns(7);
Conditionally matching an argument
calculator.Received().Add(1, Arg.Is<int>(x => x < 0));
Matching a specific argument
calculator.Add(0, 42);
![Page 17: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/17.jpg)
Void calls and When..Do Returns() can be used to get callbacks for members that return a
value, but for void members we need a different technique, because we can’t call a method on a void return. For these cases we can use the When..Do syntax.
public interface IFoo
{
void SayHello(string to);
}
[TestMethod]
public void SayHello()
{
var counter = 0;
var foo = Substitute.For<IFoo>();
foo.When(x => x.SayHello("World"))
.Do(x => counter++);
foo.SayHello("World");
foo.SayHello("World");
Assert.AreEqual(2, counter);
}
![Page 18: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/18.jpg)
Throwing exceptions
Callbacks can be used to throw exceptions when a member is called.
var calculator = Substitute.For<ICalculator>();
//For non-voids:
calculator.Add(-1, -1).Returns(x => { throw new Exception(); });
//For voids and non-voids:
calculator.When(x => x.Add(-2, -2))
.Do(x => { throw new Exception(); });
//Both calls will now throw.
Assert.Throws<Exception>(() => calculator.Add(-1, -1));
Assert.Throws<Exception>(() => calculator.Add(-2, -2));
![Page 19: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/19.jpg)
Raising events Events are “interesting” creatures in the .NET world, as you
can’t pass around references to them like you can with other members.
Instead, you can only add or remove handlers to events
public interface IEngine
{
event EventHandler Idling;
event EventHandler<LowFuelWarningEventArgs> LowFuelWarning;
event Action<int> RevvedAt;
}
public class LowFuelWarningEventArgs : EventArgs
{
public int PercentLeft { get; private set; }
public LowFuelWarningEventArgs(int percentLeft)
{
PercentLeft = percentLeft;
}
}
![Page 20: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/20.jpg)
Cont..int numberOfEvents,revvedAt = 0;
var engine = Substitute.For<IEngine>();
engine.LowFuelWarning += (sender, args) => numberOfEvents++;
//Raise event with specific args, any sender:
engine.LowFuelWarning += Raise.EventWith(newLowFuelWarningEventArgs(10));
//Raise event with specific args and sender:
engine.LowFuelWarning += Raise.EventWith(new object(), newLowFuelWarningEventArgs(10));
engine.RevvedAt += rpm => revvedAt = rpm;
engine.RevvedAt += Raise.Event<Action<int>>(123);
Assert.AreEqual(2, numberOfEvents);
Assert.AreEqual(123, revvedAt);
![Page 21: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/21.jpg)
Auto and recursive mocks Any properties or methods that return an interface, delegate,
or purely virtual class* will automatically return substitutes themselves. This is commonly referred to as recursive mocking.
It is useful because you can avoid explicitly creating each substitute, which means less code.
public interface IContext
{
IRequest CurrentRequest { get; }
}
public interface IRequest
{
IIdentity Identity { get; }
IIdentity NewIdentity(string name);
}
public interface IIdentity
{
string Name { get; }
string[] Roles();
}
![Page 22: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/22.jpg)
var context = Substitute.For<IContext>();
context.CurrentRequest.Identity.Name.Returns("My pet fish Eric");
Assert.AreEqual("My pet fish Eric",
context.CurrentRequest.Identity.Name);
Here CurrentRequest is automatically going to return a
substitute for IRequest, and the IRequest substitute will
automatically return a substitute for IIdentity.
Cont..
![Page 23: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/23.jpg)
Setting out and ref args
Out and ref arguments can be set using a Returns() callback,
or using When..Do.
public interface ILookup
{
bool TryLookup(string key, out string value);
}
For the interface above we can configure the return value and
set the output of the second argument
![Page 24: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/24.jpg)
//Arrange
var value = "";
var lookup = Substitute.For<ILookup>();
lookup
.TryLookup("hello", out value)
.Returns(x =>
{
x[1] = "world!";
return true;
});
//Act
var result = lookup.TryLookup("hello", out value);
//Assert
Assert.True(result);
Assert.AreEqual(value, "world!");
Cont..
![Page 25: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/25.jpg)
Checking call order Sometimes calls need to be made in a specific order.
Depending on the timing of calls like this is known as temporal coupling.
Ideally we’d change our design to remove this coupling, but for times when we can’t NSubstitute lets us resort to asserting the order of calls.
[Test]
public void TestCommandRunWhileConnectionIsOpen()
{
var connection = Substitute.For<IConnection>();
var command = Substitute.For<ICommand>();
var subject = new Controller(connection, command);
subject.DoStuff();
Received.InOrder(() =>{
connection.Open();
command.Run(connection);
connection.Close();});
}
![Page 26: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/26.jpg)
Partial Stubs
Partial substitutes allow us to create an object that acts like a real instance of a class, and selectively substitute for specific parts of that object.
This is useful for when we need a substitute to have real behavior except for a single method that we want to replace, or when we just want to spy on what calls are being made.
public class SummingReader
{
public virtual int Read(string path)
{
var s = ReadFile(path);
return s.Split(',').Select(int.Parse).Sum();
}
public virtual string ReadFile(string path) { return "the result of reading the file here"; }
}
![Page 27: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/27.jpg)
Cont…[Test]
public void ShouldSumAllNumbersInFile()
{
var reader = Substitute.ForPartsOf<SummingReader>();
reader.ReadFile(Arg.Is("foo.txt")).Returns("1,2,3,4,5");
var result = reader.Read("foo.txt");
Assert.That(result, Is.EqualTo(15));
}
![Page 28: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/28.jpg)
Advantages of NSubstitute
It works really well with TDD.
You do not have to pay the ‘lambda tax’ which is present in
RhinoMocks, Moq and Fake it easy.
NSubstitute uses c# language features in a really clever way
to simplify usage.
◦ This results in each interaction requiring less code and
being easier to read.
![Page 29: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/29.jpg)
Alternatives
Moq
JustMock
FakeItEasy
EasyMock.NET
NMock 3
Rhino Mocks
![Page 30: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/30.jpg)
Q & A
![Page 31: Introduction to nsubstitute](https://reader033.fdocuments.net/reader033/viewer/2022052413/55a934171a28ab3a368b48ea/html5/thumbnails/31.jpg)
Thank you