Git Product home page Git Product logo

nsubstitute's Introduction

NSubstitute

Build status Nuget

Visit the NSubstitute website for more information.

What is it?

NSubstitute is designed as a friendly substitute for .NET mocking libraries.

It is an attempt to satisfy our craving for a mocking library with a succinct syntax that helps us keep the focus on the intention of our tests, rather than on the configuration of our test doubles. We've tried to make the most frequently required operations obvious and easy to use, keeping less usual scenarios discoverable and accessible, and all the while maintaining as much natural language as possible.

Perfect for those new to testing, and for others who would just like to to get their tests written with less noise and fewer lambdas.

Installation

Getting help

If you have questions, feature requests or feedback on NSubstitute please raise an issue on our project site. All questions are welcome via our project site, but for "how-to"-style questions you can also try StackOverflow with the [nsubstitute] tag, which often leads to very good answers from the larger programming community. StackOverflow is especially useful if your question also relates to other libraries that our team may not be as familiar with (e.g. NSubstitute with Entity Framework). You can also head on over to the NSubstitute discussion group if you prefer.

Basic use

Let's say we have a basic calculator interface:

public interface ICalculator
{
    int Add(int a, int b);
    string Mode { get; set; }
    event Action PoweringUp;
}

We can ask NSubstitute to create a substitute instance for this type. We could ask for a stub, mock, fake, spy, test double etc., but why bother when we just want to substitute an instance we have some control over?

_calculator = Substitute.For<ICalculator>();

โš ๏ธ Note: NSubstitute will only work properly with interfaces or with virtual members of classes. Be careful substituting for classes with non-virtual members. See Creating a substitute for more information.

Now we can tell our substitute to return a value for a call:

_calculator.Add(1, 2).Returns(3);
Assert.That(_calculator.Add(1, 2), Is.EqualTo(3));

We can check that our substitute received a call, and did not receive others:

_calculator.Add(1, 2);
_calculator.Received().Add(1, 2);
_calculator.DidNotReceive().Add(5, 7);

If our Received() assertion fails, NSubstitute tries to give us some help as to what the problem might be:

NSubstitute.Exceptions.ReceivedCallsException : Expected to receive a call matching:
    Add(1, 2)
Actually received no matching calls.
Received 2 non-matching calls (non-matching arguments indicated with '*' characters):
    Add(1, *5*)
    Add(*4*, *7*)

We can also work with properties using the Returns syntax we use for methods, or just stick with plain old property setters (for read/write properties):

_calculator.Mode.Returns("DEC");
Assert.That(_calculator.Mode, Is.EqualTo("DEC"));

_calculator.Mode = "HEX";
Assert.That(_calculator.Mode, Is.EqualTo("HEX"));

NSubstitute supports argument matching for setting return values and asserting a call was received:

_calculator.Add(10, -5);
_calculator.Received().Add(10, Arg.Any<int>());
_calculator.Received().Add(10, Arg.Is<int>(x => x < 0));

We can use argument matching as well as passing a function to Returns() to get some more behaviour out of our substitute (possibly too much, but that's your call):

_calculator
   .Add(Arg.Any<int>(), Arg.Any<int>())
   .Returns(x => (int)x[0] + (int)x[1]);
Assert.That(_calculator.Add(5, 10), Is.EqualTo(15));

Returns() can also be called with multiple arguments to set up a sequence of return values.

_calculator.Mode.Returns("HEX", "DEC", "BIN");
Assert.That(_calculator.Mode, Is.EqualTo("HEX"));
Assert.That(_calculator.Mode, Is.EqualTo("DEC"));
Assert.That(_calculator.Mode, Is.EqualTo("BIN"));

Finally, we can raise events on our substitutes (unfortunately C# dramatically restricts the extent to which this syntax can be cleaned up):

bool eventWasRaised = false;
_calculator.PoweringUp += () => eventWasRaised = true;

_calculator.PoweringUp += Raise.Event<Action>();
Assert.That(eventWasRaised);

Building

NSubstitute and its tests can be compiled and run using Visual Studio and Visual Studio for Mac. Note that some tests are marked [Pending] and are not meant to pass at present, so it is a good idea to exclude tests in the Pending category from test runs.

There are also build scripts in the ./build directory for command line builds, and CI configurations in the project root.

To do full builds you'll also need Ruby, as the jekyll gem is used to generate the website.

Other libraries you may be interested in

  • Moq: the original Arrange-Act-Assert mocking library for .NET, and a big source of inspiration for NSubstitute.
  • FakeItEasy: another modern mocking library for .NET. If you're not sold on NSubstitute's syntax, try FIE!
  • substitute.js: a mocking library for TypeScript inspired by NSubstitute's syntax (@fluffy-spoon/substitute on NPM)

nsubstitute's People

Contributors

304notmodified avatar alexandrnikitin avatar anaisbetts avatar anthonyegerton avatar asbjornu avatar bartoszgolek avatar brad avatar dtchepak avatar eric-winkler avatar geirsagberg avatar havunen avatar i-e-b avatar icalvo avatar jbialobr avatar jheinath avatar joaopgrassi avatar kevinbosman avatar krzysztofbranicki avatar m3zercat avatar mrinaldi avatar robdmoore avatar romfos avatar shiftkey avatar shiftkey-tester avatar socolin avatar trullock avatar x789 avatar xerxesb avatar zlangner avatar zvirja avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nsubstitute's Issues

Exception message review

Some exceptions do not have decent messages. Should review all nsub exceptions and make sure they are thrown with a decent explanation.

Also should update formatting for arg specs that use an Expression<>.

Auto-subbing from Func delegates

  public interface IFoo { }

  [Test]
  public void AutosubFromFuncs()
  {
      var x = Substitute.For<Func<IFoo>>();
      Assert.That(x(), Is.Not.Null);
  }

Documentation (ongoing)

Keep fleshing out docs passed v1.0 as required. (Based on feedback, usergroup questions etc).

Not picking up ambiguous arg matchers properly

public interface ICalculator { int Add(int a, int b); }

[Test] public void a_test() {
    var calculator = Substitute.For<ICalculator>();
    calculator.Add(0, Arg.Any<int>()).Returns(5);
    Assert.AreEqual(5, calculator.Add(0, 1));        
}

This test fails at the Assert.AreEqual because it gets 0, not 5.

I expected to get an AmbiguousArgumentsException because it could not tell which argument the Arg.Any<int>() arg matcher applied to. Either that or have the test pass because I called Add(0, 1) which I stubbed to give 5.

Raising delegate (non-EventHandler) events

Event is declared as delegate (i.e. not an EventHandler). So can't use Raise.Action or Raise.Event.

    [Test]
    public void CanRaiseNotifyPropertyChangedEvent()
    {
        var sub = Substitute.For<INotifyPropertyChanged>();
        sub.PropertyChanged += Raise.Event(); // <---Won't compile
    }

Another example is: _AppDomain.UnhandledException event

Serialising substitutes

Reported on the mailing list.

using NSubstitute;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SerializeNSubstitute
{
       class Program
       {
               static void Main(string[] args)
               {
                       var root = Substitute.For<Root>();
                       var formatter = new BinaryFormatter();
                       Root newRoot = null;

                       using(var stream = new MemoryStream())
                       {
                               formatter.Serialize(stream, root);
                               stream.Position = 0;
                               newRoot = formatter.Deserialize(stream) as Root;
                       }
               }
       }

       [Serializable]
       public abstract class Root
       {
               public abstract string Data { get; set; }
       }
}

When I run this, I get the following error (happens at the Serialize()
call):

System.MethodAccessException was unhandled
Message=Attempt by method
'Castle.Proxies.RootProxy.GetObjectData(System.Runtime.Serialization.SerializationInfo,
System.Runtime.Serialization.StreamingContext)' to access method
'Castle.DynamicProxy.Generators.Emitters.TypeUtil.Sort(System.Reflection.MemberInfo[])'
failed.
Source=DynamicProxyGenAssembly2
StackTrace:
at Castle.Proxies.RootProxy.GetObjectData(SerializationInfo ,
StreamingContext )
at
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object
obj, ISurrogateSelector surrogateSelector, StreamingContext context,
SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter,
ObjectWriter objectWriter, SerializationBinder binder)
at
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object
obj, ISurrogateSelector surrogateSelector, StreamingContext context,
SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter,
ObjectWriter objectWriter, SerializationBinder binder)
at
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object
graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
at
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream
serializationStream, Object graph, Header[] headers, Boolean fCheck)
at
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream
serializationStream, Object graph)
at SerializeNSubstitute.Program.Main(String[] args) in G:
\JasonBock\Personal.NET Projects\SerializeNSubstitute\Program.cs:line
18
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly,
String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile,
Evidence assemblySecurity, String[] args)
at
Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object
state)
at System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state, Boolean
ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:

I looked at the embedded Castle.DynamicProxy stuff in the NSubstitute
DLL and the Sort() method exists, but this is occuring in the proxy
itself, so I'm not sure what's going on there.

Property behaviour for indexers don't work

public interface IFoo { string this[string s] { get; set; } }

[Test]
public void Props()
{
    var foo = Substitute.For<IFoo>();
    foo["hi"] = "howdy";
    Assert.That(foo["hi"], Is.EqualTo("howdy"));
}

Thanks to @rbanks54 for the bug report.

Raising delegate as an event.

Trying to raise a custom event handler definition I got a compilation error. The following code illustrates the issue.

namespace NSubstitute.Issue
{
public delegate void WeirdHandler(WeirdEventArgs args);

public interface ISomething
{
    event WeirdHandler SomethingHappened;
}

public class WeirdEventArgs
{
}

public class Scenario
{
    private ISomething _something;

    [SetUp]
    public void Setup()
    {
        _something = Substitute.For<ISomething>();

        _something.SomethingHappened += Raise.Event(new WeirdEventArgs());//This won't compile
    }

    [Test]
    public void ShouldDoSomethingInResponseToTheEvent()
    {
        ////left in blank for your imagination.
    }
}

}

For Received() exception show which args do not match

When listing actual calls to sub, delineate the args that do not match call spec with '*' characters.

E.g. Expected:
mySub.Something(IController)
Actual (non-matching arguments marked with _'s):
mySub.Something(_IController*)

NullReferenceException when creating a sub for SynchronizationContext

How to reproduce:
Substitute.For<SynchronizationContext>();

Exception:
System.NullReferenceException : Object reference not set to an instance of an object.
at Castle.DynamicProxy.AttributeUtil.d__0.MoveNext() in c:\TeamCity\buildAgent\work\f0c43d96621436a8\src\Castle.Core\DynamicProxy\AttributeUtil.cs: line 150
at Castle.DynamicProxy.Contributors.ClassProxyInstanceContributor.Generate(ClassEmitter class, ProxyGenerationOptions options) in c:\TeamCity\buildAgent\work\f0c43d96621436a8\src\Castle.Core\DynamicProxy\Contributors\ClassProxyInstanceContributor.cs: line 65
at Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateType(String name, Type[] interfaces, INamingScope namingScope) in c:\TeamCity\buildAgent\work\f0c43d96621436a8\src\Castle.Core\DynamicProxy\Generators\ClassProxyGenerator.cs: line 122
at Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateCode(Type[] interfaces, ProxyGenerationOptions options) in c:\TeamCity\buildAgent\work\f0c43d96621436a8\src\Castle.Core\DynamicProxy\Generators\ClassProxyGenerator.cs: line 88
at Castle.DynamicProxy.DefaultProxyBuilder.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options) in c:\TeamCity\buildAgent\work\f0c43d96621436a8\src\Castle.Core\DynamicProxy\DefaultProxyBuilder.cs: line 79
at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options) in c:\TeamCity\buildAgent\work\f0c43d96621436a8\src\Castle.Core\DynamicProxy\ProxyGenerator.cs: line 1360
at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors) in c:\TeamCity\buildAgent\work\f0c43d96621436a8\src\Castle.Core\DynamicProxy\ProxyGenerator.cs: line 1300
at NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory.GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments) in c:\dev\other\NSubstitute\Source\NSubstitute\Proxies\CastleDynamicProxy\CastleDynamicProxyFactory.cs: line 34
at NSubstitute.Proxies.ProxyFactory.GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments) in c:\dev\other\NSubstitute\Source\NSubstitute\Proxies\ProxyFactory.cs: line 20
at NSubstitute.Core.SubstituteFactory.Create(Type[] typesToProxy, Object[] constructorArguments) in c:\dev\other\NSubstitute\Source\NSubstitute\Core\SubstituteFactory.cs: line 26
at NSubstitute.Substitute.For(Type[] typesToProxy, Object[] constructorArguments) in c:\dev\other\NSubstitute\Source\NSubstitute\Substitute.cs: line 71
at NSubstitute.Substitute.For(Object[] constructorArguments) in c:\dev\other\NSubstitute\Source\NSubstitute\Substitute.cs: line 22
at Cochlear.UnitTests.NSubSampleCase.SyncContext() in NSubSampleCase.cs: line 12

Exception when using null parameter for raising events

With this definition
public delegate void ProgressEventHandler(int progress, string Message);

When raising event like this
Subject.OnProgress += Raise.Event<ProgressEventHandler>(57, null);

I get the following exception

System.NullReferenceException : Object reference not set to an instance of an object.
at NSubstitute.DelegateEventWrapper1.RequiredArgsHaveBeenProvided(Object[] providedArgs, ParameterInfo[] requiredArgs) at NSubstitute.DelegateEventWrapper1.WorkOutRequiredArguments(ICall call)
at NSubstitute.RaiseEventWrapper.<>c__DisplayClass1.b__0(ICall call)
at NSubstitute.Routing.Handlers.RaiseEventHandler.Handle(ICall call)
at NSubstitute.Routing.Route.<>c__DisplayClass3.b__0(ICallHandler x)
at System.Linq.Enumerable.<>c__DisplayClass123.<CombineSelectors>b__11(TSource x) at System.Linq.Enumerable.WhereSelectArrayIterator2.MoveNext()
at System.Linq.Enumerable.FirstOrDefault(IEnumerable1 source, Func2 predicate)
at NSubstitute.Routing.Route.Handle(ICall call)
at NSubstitute.Core.CallRouter.Route(ICall call)
at NSubstitute.Proxies.CastleDynamicProxy.CastleForwardingInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()

Improve matching of arg matcher/arg combinations

e.g. For interface ICalculator { int Add(int a, int b); }
calculator.Add(10, Arg.Any());

NSub should be able to work out which param the arg matcher relates to (as 10 is not default(int)). Currently it throws ambiguous match and forces you to use Arg.Is(10).

Ordered mocks

See [Pending] acceptance tests.
May need syntax review.

CallInfo.Arg<T>() fails for null argument

sub.When(x => sub.Call(myFoo)).Do(x => foo = x.Arg<IFoo>())

Fails when myFoo is null, I think because the code tries to search an object[] for a type Where(x => x is IFoo).

Params arg matching bug

Following test fails with System.InvalidCastException : Unable to cast object of type 'nsubrepro.Specification1[nsubrepro.User][]' to type 'nsubrepro.Specification1[nsubrepro.User]'.. Is trying to treat the single arg spec as the params array.

[TestFixture]
public class Tests
{
    [Test]
    public void Repro()
    {
        var repository = Substitute.For<IRepository<User>>();
        //repository.Find(Arg.Any<EmailMatchSpecification>()).Returns(new List<User>{new User{Email = "[email protected]", Username = "Test"}});
        repository
           .Find(Arg.Is<Specification<User>>(x => x.GetType() == typeof(EmailMatchSpecification)))
           .Returns(new List<User>{new User{Email = "[email protected]", Username = "Test"}});

        var results = repository.Find(new UsernameMatchSpecification("bob"));

        Assert.AreEqual(0, results.Count());

    }
}

public interface IRepository<T>
{
    IEnumerable<T> Find(params Specification<T>[] specifications);
}
public class User 
{
    public string Email { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
}

public class EmailMatchSpecification : Specification<User>
{
    private readonly string _emailAddress;
    public EmailMatchSpecification(string emailAddress)
    {
        _emailAddress = emailAddress;
    }
    public override Expression<Func<User, bool>> Predicate
    {
        get { return u => u.Email != null && u.Email.ToLower() == _emailAddress.ToLower().Trim(); }
    }
}

public class UsernameMatchSpecification : Specification<User>
{
    private readonly string _username;
    public UsernameMatchSpecification(string username)
    {
        _username = username;
    }
    public override Expression<Func<User, bool>> Predicate
    {
        get { return u => u.Username == _username; }
    }
}
public abstract class Specification<T>
{
    private Func<T, bool> _compiledPredicate;
    Func<T, bool> CompiledPredicate
    {
        get
        {
            _compiledPredicate = _compiledPredicate ?? Predicate.Compile();

            return _compiledPredicate;
        }
    }
    public bool IsSatisfiedBy(T item)
    {
        return CompiledPredicate(item);
    }
    public IEnumerable<T> SatisfyingElements(IEnumerable<T> candidates)
    {
        return candidates.Where(IsSatisfiedBy);
    }
    public abstract Expression<Func<T, bool>> Predicate { get; }
}

Re-entrant sub creation causes LastCallRouter to be incorrectly cleared

Setting a Returns() within a Returns() leads to global LastCallRouter being incorrect. May need to look at using a stack for LastCallRouter so we can handle this.

Example of bug:
sub.SomeCall().Returns(CreateASub2());
...
ISub2 CreateASub2() {
var sub2 = Substitute.For();
sub2.OtherCall().Returns(3); // <--- Problem here
}

Working example thanks to Xerx:
http://github.com/xerxesb/NSubstitute/commit/e9e2e3d2cedf2319045ebf9c529cee890b0e061d

public void Should_set_both_calls_to_return_the_specified_values()

Subbing for Func<int>

Subbing for Func is giving strange null ref exs:

        var func = Substitute.For<Func<int>>();
        func().Returns(10);
        Assert.AreEqual(10, func());

Repro in 139d39

Search documentation

Google Custom Search Engine (or other) for search from NSub help.

Possibly try out jekyll auto-gen'd related links and see if they are helpful.

PERF: IgnoreCallRouterCallsHook should override Equals and GetHashCode

Castle.DynamicProxy Warning: 0 : The IProxyGenerationHook type NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory+IgnoreCallRouterCallsHook does not override both Equals and GetHashCode. If these are not correctly overridden caching will fail to work causing performance problems.

Query model for received calls

Adding this as a placeholder for the feature while it is being planned.

Rather than expanding the API to support things like "received 4 times", it might be better to encapsulate that in a nice query model. Currently have ReceivedCalls() : IEnumerable as a starting point, but will need to add some convenience methods for selecting calls to particular members (probably using lambdas/expressions).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.