.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. }); 

 

博文最后更新时间:


评论

  • Merziuz

    PuKDQD http://pills2sale.com/ viagra online

  • Merziuz

    PYN6fj http://pills2sale.com/ viagra online

  • Collin

    I can't get through at the moment <a href="http://thewhitedogbistro.com/can-clindamycin-raise-blood-sugar-08vo">clindamycin phosphate topical solution usp 1 pledgets</a> * Feintool said first-half net profit slid 15percent to 5.4 million francs and that it expects roughly 480million francs in full-year sales and a slightly lower operatingmargin on the year due to development costs and investment inthe start-up of new production.

  • Foster

    A law firm <a href="https://thaipham.live/san-estrodex-side-effects-b7ta">san estrodex side effects</a> A source tells Confidenti@l that in usual Kris style, she had her camp email Brand Placement L.A. — which helps companies get products on TV, in movies and into the hands of celebs — saying they were “canvassing” people and businesses to contribute gifts for Kylie’s birthday bash.

  • Rubin

    How long are you planning to stay here? <a href="https://conference.ue-varna.bg/emc/smart-pill-box-for-elderly-zcvh">party smart pill side effects</a> "Henri Matisse: The Cut-Outs", which opens at Tate Modern in London in April, and afterwards goes to the Museum of Modern Art in New York, assembles some 120 of Matisse's fragile but striking cutouts, more than have ever been presented in one place. They ranging in size from miniatures to works that cover an entire wall.

  • Julius

    I love this site <a href="https://avanti.cze.pl/difference-between-desvenlafaxine-and-venlafaxine-ev71">goodrx venlafaxine</a> “There’s been a few times now I’ve had to come out early because I’m throwing 20 pitches an inning,” Wheeler said. “Falling behind guys like that is not going to work. That’s part of my game. I’ll have to fix it pretty fast.

发表评论

博客统计

访问量:5263897

博文总数:750 评论总数:910100

原创126 翻译20 转载604