.NET测试--模拟框架NSubstitute

.NET测试--模拟框架NSubstitute

NSubstitute在GitHub的开源地址:https://github.com/nsubstitute/nsubstitute/downloads

入门

现在假设我们有一个基本的计算器界接口:

  1. public interface ICalculator 

  2. int Add(int a, int b)
  3. string Mode { get; set; } 
  4. event EventHandler PoweringUp; 

使用NSubstitute创建一个替代实例。
我们可以使用stub(存根)、mock(模拟)、fake、spy、test double等。

  1. calculator = Substitute.For<ICalculator>(); 

现在我们可以使用calculator对象返回一个计算值:

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

检测calculator对象是否执行了一个方法调用,没有执行其他的调用:

  1. calculator.Add(1, 2); 
  2. calculator.Received().Add(1, 2);//是否执行了调用 
  3. calculator.DidNotReceive().Add(5, 7);//是否没有执行调用 

使用Returns方法,返回执行调用的返回值。

  1. calculator.Mode.Returns("DEC"); 
  2. Assert.That(calculator.Mode, Is.EqualTo("DEC")); 
  3.  
  4. calculator.Mode = "HEX"
  5. Assert.That(calculator.Mode, Is.EqualTo("HEX")); 

NSubstitute 支持设置输入参数匹配:

  1. calculator.Add(10, -5); 
  2. calculator.Received().Add(10, Arg.Any<int>());//任何int参数 
  3. calculator.Received().Add(10, Arg.Is<int>(x => x < 0));//第二个参数小于零 

使用参数匹配以及讲函数传递给Returns方法:

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

Returns方法返回多参数序列:

  1. calculator.Mode.Returns("HEX", "DEC", "BIN"); 
  2. Assert.That(calculator.Mode, Is.EqualTo("HEX")); 
  3. Assert.That(calculator.Mode, Is.EqualTo("DEC")); 
  4. Assert.That(calculator.Mode, Is.EqualTo("BIN")); 

引发事件:

  1. bool eventWasRaised = false
  2. calculator.PoweringUp += (sender, args) => eventWasRaised = true
  3. calculator.PoweringUp += Raise.Event(); 
  4. Assert.That(eventWasRaised); 

创建substitute

创建一个具有构造函数参数的类的替代:

  1. var someClass = Substitute.For<SomeClassWithCtorArgs>(5, "hello world"); 

多接口

  1. var command = Substitute.For<ICommand, IDisposable>(); 
  2. var runner = new CommandRunner(command); 
  3.  
  4. runner.RunCommand(); 
  5.  
  6. command.Received().Execute(); 
  7. ((IDisposable)command).Received().Dispose(); 

可以设置多接口,但是类只能有一个:

  1. var substitute = Substitute.For( 
  2. new[] { typeof(ICommand), typeof(ISomeInterface), typeof(SomeClassWithCtorArgs) }, 
  3. new object[] { 5, "hello world"
  4. ); 
  5. Assert.IsInstanceOf<ICommand>(substitute); 
  6. Assert.IsInstanceOf<ISomeInterface>(substitute); 
  7. Assert.IsInstanceOf<SomeClassWithCtorArgs>(substitute); 

代理

  1. var func = Substitute.For<Func<string>>(); 
  2. func().Returns("hello"); 
  3. Assert.AreEqual("hello", func()); 

设置返回值

  1. public interface ICalculator
  2. int Add(int a, int b)
  3. string Mode { get; set; } 

方法

  1. var calculator = Substitute.For<ICalculator>(); 
  2. calculator.Add(1, 2).Returns(3); 

调用的参数必须一样,返回值才会固定

  1. //Make a call return 3: 
  2. calculator.Add(1, 2).Returns(3); 
  3. Assert.AreEqual(calculator.Add(1, 2), 3); 
  4. Assert.AreEqual(calculator.Add(1, 2), 3); 
  5.  
  6. //Call with different arguments does not return 3 
  7. Assert.AreNotEqual(calculator.Add(3, 6), 3); 

属性

  1. calculator.Mode.Returns("DEC"); 
  2. Assert.AreEqual(calculator.Mode, "DEC"); 
  3. calculator.Mode = "HEX"
  4. Assert.AreEqual(calculator.Mode, "HEX"); 

返回值指定输入具体参数

  1. //Return when first arg is anything and second arg is 5: 
  2. //返回当第一个参数是任意int,第二个参数是5 
  3. calculator.Add(Arg.Any<int>(), 5).Returns(10); 
  4. Assert.AreEqual(10, calculator.Add(123, 5)); 
  5. Assert.AreEqual(10, calculator.Add(-9, 5)); 
  6. Assert.AreNotEqual(10, calculator.Add(-9, -9)); 
  7.  
  8. //Return when first arg is 1 and second arg less than 0: 
  9. //返回当第一个参数是1,第二个参数小于零 
  10. calculator.Add(1, Arg.Is<int>(x => x < 0)).Returns(345); 
  11. Assert.AreEqual(345, calculator.Add(1, -2)); 
  12. Assert.AreNotEqual(345, calculator.Add(1, 2)); 
  13.  
  14. //Return when both args equal to 0: 
  15. //返回当两个参数都等于0 
  16. calculator.Add(Arg.Is(0), Arg.Is(0)).Returns(99); 
  17. Assert.AreEqual(99, calculator.Add(0, 0)); 

返回值指定输入任意参数

  1. calculator.Add(1, 2).ReturnsForAnyArgs(100);  
  2. Assert.AreEqual(calculator.Add(1, 2), 100); 
  3. Assert.AreEqual(calculator.Add(-7, 15), 100); 

从一个方法获取返回值

  1. calculator 
  2. .Add(Arg.Any<int>(), Arg.Any<int>()) 
  3. .Returns(x => (int)x[0] + (int)x[1]);//从一个方法获取返回值 
  4.  
  5. Assert.That(calculator.Add(1, 1), Is.EqualTo(2)); 
  6. Assert.That(calculator.Add(20, 30), Is.EqualTo(50)); 
  7. Assert.That(calculator.Add(-73, 9348), Is.EqualTo(9275)); 

调用信息

Arg<T>()
ArgAt<T>(int position)

  1. public interface IFoo
  2. string Bar(int a, string b)

  3.  
  4. var foo = Substitute.For<IFoo>(); 
  5. foo.Bar(0, "").ReturnsForAnyArgs(x => "Hello " + x.Arg<string>()); 
  6. Assert.That(foo.Bar(1, "World"), Is.EqualTo("Hello World")); 

回调

  1. var counter = 0
  2. calculator 
  3. .Add(0, 0
  4. .ReturnsForAnyArgs(x => { 
  5. counter++; 
  6. return 0
  7. }); 
  8.  
  9. calculator.Add(7,3); 
  10. calculator.Add(2,2); 
  11. calculator.Add(11,-3); 
  12. Assert.AreEqual(counter, 3); 
  1. var counter = 0
  2. calculator 
  3. .Add(0, 0
  4. .ReturnsForAnyArgs(x => 0
  5. .AndDoes(x => counter++);//? 
  6.  
  7. calculator.Add(7,3); 
  8. calculator.Add(2,2); 
  9. Assert.AreEqual(counter, 2); 

多返回值

  1. calculator.Mode.Returns("DEC", "HEX", "BIN"); 
  2. Assert.AreEqual("DEC", calculator.Mode); 
  3. Assert.AreEqual("HEX", calculator.Mode); 
  4. Assert.AreEqual("BIN", calculator.Mode); 

多返回值回调

  1. calculator.Mode.Returns(x => "DEC", x => "HEX", x => { throw new Exception(); }); 
  2. Assert.AreEqual("DEC", calculator.Mode); 
  3. Assert.AreEqual("HEX", calculator.Mode); 
  4. Assert.Throws<Exception>(() => { var result = calculator.Mode; }); 

替换返回值

  1. calculator.Mode.Returns("DEC,HEX,OCT"); 
  2. calculator.Mode.Returns(x => "???"); 
  3. calculator.Mode.Returns("HEX"); 
  4. calculator.Mode.Returns("BIN"); 
  5. Assert.AreEqual(calculator.Mode, "BIN"); 

检查调用,是否执行,返回

  1. public interface ICommand
  2. void Execute()
  3. event EventHandler Executed; 

  4.  
  5. public class SomethingThatNeedsACommand
  6. ICommand command; 
  7. public SomethingThatNeedsACommand(ICommand command) {  
  8. this.command = command; 

  9. public void DoSomething() { command.Execute(); } 
  10. public void DontDoAnything() { } 

  11.  
  12. [Test] 
  13. public void Should_execute_command()
  14. //Arrange 
  15. var command = Substitute.For<ICommand>(); 
  16. var something = new SomethingThatNeedsACommand(command); 
  17. //Act 
  18. something.DoSomething(); 
  19. //Assert 检查是否执行了这个方法 
  20. command.Received().Execute(); 

检查一个调用是否未执行(未返回)

  1. var command = Substitute.For<ICommand>(); 
  2. var something = new SomethingThatNeedsACommand(command); 
  3. //Act 
  4. something.DontDoAnything(); 
  5. //Assert 
  6. command.DidNotReceive().Execute(); 

检查一个调用,返回执行次数

  1. public class CommandRepeater
  2. ICommand command; 
  3. int numberOfTimesToCall; 
  4. public CommandRepeater(ICommand command, int numberOfTimesToCall)
  5. this.command = command; 
  6. this.numberOfTimesToCall = numberOfTimesToCall; 

  7.  
  8. public void Execute() {  
  9. for (var i=0; i<numberOfTimesToCall; i++) command.Execute(); 


  10.  
  11. [Test] 
  12. public void Should_execute_command_the_number_of_times_specified()
  13. var command = Substitute.For<ICommand>(); 
  14. var repeater = new CommandRepeater(command, 3); 
  15. //Act 
  16. repeater.Execute(); 
  17. //Assert 
  18. command.Received(3).Execute(); // << This will fail if 2 or 4 calls were received 

带特殊参数的返回

  1. calculator.Add(1, 2); 
  2. calculator.Add(-100, 100); 
  3.  
  4. //Check received with second arg of 2 and any first arg: 
  5. calculator.Received().Add(Arg.Any<int>(), 2); 
  6. //Check received with first arg less than 0, and second arg of 100: 
  7. calculator.Received().Add(Arg.Is<int>(x => x < 0), 100); 
  8. //Check did not receive a call where second arg is >= 500 and any first arg: 
  9. calculator 
  10. .DidNotReceive() 
  11. .Add(Arg.Any<int>(), Arg.Is<int>(x => x >= 500)); 

忽略参数

  1. calculator.Add(1, 3); 
  2.  
  3. calculator.ReceivedWithAnyArgs().Add(1,1); 
  4. calculator.DidNotReceiveWithAnyArgs().Subtract(0,0); 

检查属性

  1. var mode = calculator.Mode; 
  2. calculator.Mode = "TEST"
  3.  
  4. //Check received call to property getter 
  5. //We need to assign the result to a variable to keep 
  6. //the compiler happy. 
  7. var temp = calculator.Received().Mode; 
  8.  
  9. //Check received call to property setter with arg of "TEST" 
  10. calculator.Received().Mode = "TEST"

检查索引

  1. var dictionary = Substitute.For<IDictionary<string, int>>(); 
  2. dictionary["test"] = 1
  3.  
  4. dictionary.Received()["test"] = 1
  5. dictionary.Received()["test"] = Arg.Is<int>(x => x < 5); 

检查订阅事件

  1. public class CommandWatcher
  2. ICommand command; 
  3. public CommandWatcher(ICommand command) {  
  4. command.Executed += OnExecuted; 

  5. public bool DidStuff { get; private set; } 
  6. public void OnExecuted(object o, EventArgs e) { DidStuff = true; } 
  7. }  
  8.  
  9. [Test] 
  10. public void ShouldDoStuffWhenCommandExecutes()
  11. var command = Substitute.For<ICommand>(); 
  12. var watcher = new CommandWatcher(command); 
  13.  
  14. command.Executed += Raise.Event(); 
  15.  
  16. Assert.That(watcher.DidStuff); 

  1. [Test] 
  2. public void MakeSureWatcherSubscribesToCommandExecuted()
  3. var command = Substitute.For<ICommand>(); 
  4. var watcher = new CommandWatcher(command); 
  5.  
  6. // Not recommended. Favour testing behaviour over implementation specifics. 
  7. // Can check subscription: 
  8. command.Received().Executed += watcher.OnExecuted; 
  9. // Or, if the handler is not accessible: 
  10. command.Received().Executed += Arg.Any<EventHandler>(); 

清除调用

  1. public interface ICommand
  2. void Execute()

  3.  
  4. public class OnceOffCommandRunner
  5. ICommand command; 
  6. public OnceOffCommandRunner(ICommand command)
  7. this.command = command; 

  8. public void Run()
  9. if (command == null) return
  10. command.Execute(); 
  11. command = null


  1. var command = Substitute.For<ICommand>(); 
  2. var runner = new OnceOffCommandRunner(command); 
  3.  
  4. //First run 
  5. runner.Run(); 
  6. command.Received().Execute(); 
  7.  
  8. //Forget previous calls to command 
  9. //清楚前面的调用 
  10. command.ClearReceivedCalls(); 
  11.  
  12. //Second run 
  13. runner.Run(); 
  14. command.DidNotReceive().Execute(); 

参数匹配

忽略参数

  1. calculator.Add(Arg.Any<int>(), 5).Returns(7); 
  2.  
  3. Assert.AreEqual(7, calculator.Add(42, 5)); 
  4. Assert.AreEqual(7, calculator.Add(123, 5)); 
  5. Assert.AreNotEqual(7, calculator.Add(1, 7)); 
  6.  
  7.  
  8. formatter.Format(new object()); 
  9. formatter.Format("some string"); 
  10.  
  11. formatter.Received().Format(Arg.Any<object>()); 
  12. formatter.Received().Format(Arg.Any<string>()); 
  13. formatter.DidNotReceive().Format(Arg.Any<int>()); 

匹配一个参数

  1. calculator.Add(1, -10); 
  2.  
  3. //Received call with first arg 1 and second arg less than 0: 
  4. calculator.Received().Add(1, Arg.Is<int>(x => x < 0)); 
  5. //Received call with first arg 1 and second arg of -2, -5, or -10: 
  6. calculator 
  7. .Received() 
  8. .Add(1, Arg.Is<int>(x => new[] {-2,-5,-10}.Contains(x))); 
  9. //Did not receive call with first arg greater than 10: 
  10. calculator.DidNotReceive().Add(Arg.Is<int>(x => x > 10), -10); 
  11.  
  12. formatter.Format(Arg.Is<string>(x => x.Length <= 10)).Returns("matched"); 
  13.  
  14. Assert.AreEqual("matched", formatter.Format("short")); 
  15. Assert.AreNotEqual("matched", formatter.Format("not matched, too long")); 
  16. // Will not match because trying to access .Length on null will throw an exception when testing 
  17. // our condition. NSubstitute will assume it does not match and swallow the exception. 
  18. Assert.AreNotEqual("matched", formatter.Format(null)); 

匹配特殊参数

  1. calculator.Add(0, 42); 
  2.  
  3. //This won't work; NSubstitute isn't sure which arg the matcher applies to: 
  4. //calculator.Received().Add(0, Arg.Any<int>()); 
  5.  
  6. calculator.Received().Add(Arg.Is(0), Arg.Any<int>()); 
  7.  

使用参数匹配

  1. /* ARRANGE */ 
  2.  
  3. var widgetFactory = Substitute.For<IWidgetFactory>(); 
  4. var subject = new Sprocket(widgetFactory); 
  5.  
  6. // OK: Use arg matcher for a return value: 
  7. widgetFactory.Make(Arg.Is<int>(x => x > 10)).Returns(TestWidget); 
  8.  
  9. /* ACT */ 
  10.  
  11. // NOT OK: arg matcher used with a real call: 
  12. // subject.StartWithWidget(Arg.Any<int>()); 
  13.  
  14. // Use a real argument instead: 
  15. subject.StartWithWidget(4); 
  16.  
  17. /* ASSERT */ 
  18.  
  19. // OK: Use arg matcher to check a call was received: 
  20. widgetFactory.Received().Make(Arg.Is<int>(x => x > 0)); 

回调,空调用

  1. var counter = 0
  2. calculator 
  3. .Add(0,0
  4. .ReturnsForAnyArgs(x => 0
  5. .AndDoes(x => counter++); 
  6.  
  7. calculator.Add(7,3); 
  8. calculator.Add(2,2); 
  9. calculator.Add(11,-3); 
  10. Assert.AreEqual(counter, 3); 

when...do...

  1. public interface IFoo
  2. void SayHello(string to)

  3. [Test] 
  4. public void SayHello()
  5. var counter = 0
  6. var foo = Substitute.For<IFoo>(); 
  7. foo.When(x => x.SayHello("World")) 
  8. .Do(x => counter++); 
  9.  
  10. foo.SayHello("World"); 
  11. foo.SayHello("World"); 
  12. Assert.AreEqual(2, counter); 

复杂调用

  1. var sub = Substitute.For<ISomething>(); 
  2.  
  3. var calls = new List<string>(); 
  4. var counter = 0
  5.  
  6. sub 
  7. .When(x => x.Something()) 
  8. .Do( 
  9. Callback.First(x => calls.Add("1")) 
  10. .Then(x => calls.Add("2")) 
  11. .Then(x => calls.Add("3")) 
  12. .ThenKeepDoing(x => calls.Add("+")) 
  13. .AndAlways(x => counter++) 
  14. ); 
  15.  
  16. for (int i = 0; i < 5; i++) 

  17. sub.Something(); 

  18. Assert.That(String.Concat(calls), Is.EqualTo("123++")); 
  19. Assert.That(counter, Is.EqualTo(5)); 

抛出异常

  1. //For non-voids: 
  2. calculator.Add(-1, -1).Returns(x => { throw new Exception(); }); 
  3.  
  4. //For voids and non-voids: 
  5. calculator 
  6. .When(x => x.Add(-2, -2)) 
  7. .Do(x => { throw new Exception(); }); 
  8.  
  9. //Both calls will now throw. 
  10. Assert.Throws<Exception>(() => calculator.Add(-1, -1)); 
  11. Assert.Throws<Exception>(() => calculator.Add(-2, -2)); 

事件

  1. public interface IEngine
  2. event EventHandler Idling; 
  3. event EventHandler<LowFuelWarningEventArgs> LowFuelWarning; 
  4. event Action<int> RevvedAt; 

  5.  
  6. public class LowFuelWarningEventArgs : EventArgs
  7. public int PercentLeft { get; private set; } 
  8. public LowFuelWarningEventArgs(int percentLeft)
  9. PercentLeft = percentLeft; 


  1. var wasCalled = false
  2. //设置事件 
  3. engine.Idling += (sender, args) => wasCalled = true
  4. //执行事件 
  5. //Tell the substitute to raise the event with a sender and EventArgs: 
  6. engine.Idling += Raise.EventWith(new object(), new EventArgs()); 
  7.  
  8. Assert.True(wasCalled); 

事件参数

  1. engine.LowFuelWarning += (sender, args) => numberOfEvents++; 
  2. //Raise.EventWith<TEventArgs>(...) 
  3. //Raise event with specific args, any sender: 
  4. engine.LowFuelWarning += Raise.EventWith(new LowFuelWarningEventArgs(10)); 
  5. //Raise event with specific args and sender: 
  6. engine.LowFuelWarning += Raise.EventWith(new object(), new LowFuelWarningEventArgs(10)); 
  7.  
  8. Assert.AreEqual(2, numberOfEvents); 

代理事件

  1. var sub = Substitute.For<INotifyPropertyChanged>(); 
  2. bool wasCalled = false
  3. sub.PropertyChanged += (sender, args) => wasCalled = true
  4.  
  5. sub.PropertyChanged += Raise.Event<PropertyChangedEventHandler>(this, new PropertyChangedEventArgs("test")); 
  6.  
  7. Assert.That(wasCalled); 

action事件

  1. int revvedAt = 0;; 
  2. engine.RevvedAt += rpm => revvedAt = rpm; 
  3.  
  4. engine.RevvedAt += Raise.Event<Action<int>>(123); 
  5.  
  6. Assert.AreEqual(123, revvedAt); 

自动递归模拟

递归模拟

  1. public interface INumberParser
  2. IEnumerable<int> Parse(string expression)

  3. public interface INumberParserFactory
  4. INumberParser Create(char delimiter)

  1. var factory = Substitute.For<INumberParserFactory>(); 
  2. var parser = Substitute.For<INumberParser>(); 
  3. factory.Create(',').Returns(parser); 
  4. parser.Parse("an expression").Returns(new[] {1,2,3}) 
  5. Assert.AreEqual( 
  6. factory.Create(',').Parse("an expression"), 
  7. new[] {1,2,3}); 
  8.  
  9. //or 
  10. var factory = Substitute.For<INumberParserFactory>(); 
  11. factory.Create(',').Parse("an expression").Returns(new[] {1,2,3}); 
  12. Assert.AreEqual( 
  13. factory.Create(',').Parse("an expression"), 
  14. new[] {1,2,3}); 
  15.  
  16.  
  17. var firstCall = factory.Create(','); 
  18. var secondCall = factory.Create(','); 
  19. var thirdCallWithDiffArg = factory.Create('x'); 
  20.  
  21. Assert.AreSame(firstCall, secondCall); 
  22. Assert.AreNotSame(firstCall, thirdCallWithDiffArg); 

Substitute chains(链)

  1. public interface IContext
  2. IRequest CurrentRequest { get; } 

  3. public interface IRequest
  4. IIdentity Identity { get; } 
  5. IIdentity NewIdentity(string name)

  6. public interface IIdentity {  
  7. string Name { get; }  
  8. string[] Roles(); 

  1. var context = Substitute.For<IContext>(); 
  2. context.CurrentRequest.Identity.Name.Returns("My pet fish Eric"); 
  3. Assert.AreEqual( 
  4. "My pet fish Eric",  
  5. context.CurrentRequest.Identity.Name); 

自动值

  1. var identity = Substitute.For<IIdentity>(); 
  2. Assert.AreEqual(String.Empty, identity.Name); 
  3. Assert.AreEqual(0, identity.Roles().Length); 

设置out或者ref参数

  1. public interface ILookup
  2. bool TryLookup(string key, out string value)

  1. //Arrange 
  2. var value = ""
  3. var lookup = Substitute.For<ILookup>(); 
  4. lookup 
  5. .TryLookup("hello", out value
  6. .Returns(x => {  
  7. x[1] = "world!"
  8. return true
  9. }); 
  10.  
  11. //Act 
  12. var result = lookup.TryLookup("hello", out value); 
  13.  
  14. //Assert 
  15. Assert.True(result); 
  16. Assert.AreEqual(value, "world!"); 

行为和参数匹配

执行回调

  1. public interface IOrderProcessor
  2. void ProcessOrder(int orderId, Action<bool> orderProcessed)

  3.  
  4. public class OrderPlacedCommand
  5. IOrderProcessor orderProcessor; 
  6. IEvents events; 
  7. public OrderPlacedCommand(IOrderProcessor orderProcessor, IEvents events)
  8. this.orderProcessor = orderProcessor; 
  9. this.events = events; 

  10. public void Execute(ICart cart)
  11. orderProcessor.ProcessOrder( 
  12. cart.OrderId,  
  13. wasOk => { if (wasOk) events.RaiseOrderProcessed(cart.OrderId); } 
  14. ); 


  1. [Test] 
  2. public void Placing_order_should_raise_order_processed_when_processing_is_successful()
  3. //Arrange 
  4. var cart = Substitute.For<ICart>(); 
  5. var events = Substitute.For<IEvents>(); 
  6. var processor = Substitute.For<IOrderProcessor>(); 
  7. cart.OrderId = 3
  8. //Arrange for processor to invoke the callback arg with `true` whenever processing order id 3 
  9. processor.ProcessOrder(3, Arg.Invoke(true)); 
  10.  
  11. //Act 
  12. var command = new OrderPlacedCommand(processor, events); 
  13. command.Execute(cart); 
  14.  
  15. //Assert 
  16. events.Received().RaiseOrderProcessed(3); 

用参数执行操作

  1. var argumentUsed = 0
  2. calculator.Multiply(Arg.Any<int>(), Arg.Do<int>(x => argumentUsed = x)); 
  3.  
  4. calculator.Multiply(123, 42); 
  5.  
  6. Assert.AreEqual(42, argumentUsed); 
  1. var firstArgsBeingMultiplied = new List<int>(); 
  2. calculator.Multiply(Arg.Do<int>(x => firstArgsBeingMultiplied.Add(x)), 10); 
  3.  
  4. calculator.Multiply(2, 10); 
  5. calculator.Multiply(5, 10); 
  6. calculator.Multiply(7, 4567); //Will not match our Arg.Do as second arg is not 10 
  7.  
  8. Assert.AreEqual(firstArgsBeingMultiplied, new[] { 2, 5 }); 

参数规范

  1. var numberOfCallsWhereFirstArgIsLessThan0 = 0
  2. //Specify a call where the first arg is less than 0, and the second is any int. 
  3. //When this specification is met we'll increment a counter in the Arg.Do action for  
  4. //the second argument that was used for the call, and we'll also make it return 123. 
  5. calculator 
  6. .Multiply( 
  7. Arg.Is<int>(x => x < 0),  
  8. Arg.Do<int>(x => numberOfCallsWhereFirstArgIsLessThan0++) 
  9. ).Returns(123); 
  10.  
  11. var results = new[] { 
  12. calculator.Multiply(-4, 3), 
  13. calculator.Multiply(-27, 88), 
  14. calculator.Multiply(-7, 8), 
  15. calculator.Multiply(123, 2) //First arg greater than 0, so spec won't be met. 
  16. }; 
  17.  
  18. Assert.AreEqual(3, numberOfCallsWhereFirstArgIsLessThan0); //3 of 4 calls have first arg < 0 
  19. Assert.AreEqual(results, new[] {123, 123, 123, 0}); //Last call returns 0, not 123 

检查调用命令

  1. [Test] 
  2. public void TestCommandRunWhileConnectionIsOpen()
  3. var connection = Substitute.For<IConnection>(); 
  4. var command = Substitute.For<ICommand>(); 
  5. var subject = new Controller(connection, command); 
  6.  
  7. subject.DoStuff(); 
  8.  
  9. Received.InOrder(() => { 
  10. connection.Open(); 
  11. command.Run(connection); 
  12. connection.Close(); 
  13. }); 

  1. [Test] 
  2. public void SubscribeToEventBeforeOpeningConnection()
  3. var connection = Substitute.For<IConnection>(); 
  4. connection.SomethingHappened += () => { /* some event handler */ }; 
  5. connection.Open(); 
  6.  
  7. Received.InOrder(() => { 
  8. connection.SomethingHappened += Arg.Any<Action>(); 
  9. connection.Open(); 
  10. }); 

posted @ 2017-07-21 10:22  明明.如月  阅读(1270)  评论(0编辑  收藏  举报