.NET测试--模拟框架NSubstitute

.NET测试

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. calculator.Mode = "HEX"; 

  4. 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. runner.RunCommand(); 


  4. command.Received().Execute(); 

  5. ((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. //Call with different arguments does not return 3 

  6. 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. //Return when first arg is 1 and second arg less than 0: 

  8. //返回当第一个参数是1,第二个参数小于零 

  9. calculator.Add(1, Arg.Is<int>(x => x < 0)).Returns(345); 

  10. Assert.AreEqual(345, calculator.Add(1, -2)); 

  11. Assert.AreNotEqual(345, calculator.Add(1, 2)); 


  12. //Return when both args equal to 0: 

  13. //返回当两个参数都等于0 

  14. calculator.Add(Arg.Is(0), Arg.Is(0)).Returns(99); 

  15. 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. Assert.That(calculator.Add(1, 1), Is.EqualTo(2)); 

  5. Assert.That(calculator.Add(20, 30), Is.EqualTo(50)); 

  6. 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. var foo = Substitute.For<IFoo>(); 

  4. foo.Bar(0, "").ReturnsForAnyArgs(x => "Hello " + x.Arg<string>()); 

  5. 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. calculator.Add(7,3); 

  9. calculator.Add(2,2); 

  10. calculator.Add(11,-3); 

  11. Assert.AreEqual(counter, 3); 

  1. var counter = 0; 

  2. calculator 

  3. .Add(0, 0) 

  4. .ReturnsForAnyArgs(x => 0) 

  5. .AndDoes(x => counter++);//? 


  6. calculator.Add(7,3); 

  7. calculator.Add(2,2); 

  8. 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. public class SomethingThatNeedsACommand { 

  5. ICommand command; 

  6. public SomethingThatNeedsACommand(ICommand command) {  

  7. this.command = command; 



  8. public void DoSomething() { command.Execute(); } 

  9. public void DontDoAnything() { } 




  10. [Test] 

  11. public void Should_execute_command() { 

  12. //Arrange 

  13. var command = Substitute.For<ICommand>(); 

  14. var something = new SomethingThatNeedsACommand(command); 

  15. //Act 

  16. something.DoSomething(); 

  17. //Assert 检查是否执行了这个方法 

  18. 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. public void Execute() {  

  8. for (var i=0; i<numberOfTimesToCall; i++) command.Execute(); 






  9. [Test] 

  10. public void Should_execute_command_the_number_of_times_specified() { 

  11. var command = Substitute.For<ICommand>(); 

  12. var repeater = new CommandRepeater(command, 3); 

  13. //Act 

  14. repeater.Execute(); 

  15. //Assert 

  16. 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. //Check received with second arg of 2 and any first arg: 

  4. calculator.Received().Add(Arg.Any<int>(), 2); 

  5. //Check received with first arg less than 0, and second arg of 100: 

  6. calculator.Received().Add(Arg.Is<int>(x => x < 0), 100); 

  7. //Check did not receive a call where second arg is >= 500 and any first arg: 

  8. calculator 

  9. .DidNotReceive() 

  10. .Add(Arg.Any<int>(), Arg.Is<int>(x => x >= 500)); 

忽略参数

  1. calculator.Add(1, 3); 


  2. calculator.ReceivedWithAnyArgs().Add(1,1); 

  3. calculator.DidNotReceiveWithAnyArgs().Subtract(0,0); 

检查属性

  1. var mode = calculator.Mode; 

  2. calculator.Mode = "TEST"; 


  3. //Check received call to property getter 

  4. //We need to assign the result to a variable to keep 

  5. //the compiler happy. 

  6. var temp = calculator.Received().Mode; 


  7. //Check received call to property setter with arg of "TEST" 

  8. calculator.Received().Mode = "TEST"; 

检查索引

  1. var dictionary = Substitute.For<IDictionary<string, int>>(); 

  2. dictionary["test"] = 1; 


  3. dictionary.Received()["test"] = 1; 

  4. 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. [Test] 

  9. public void ShouldDoStuffWhenCommandExecutes() { 

  10. var command = Substitute.For<ICommand>(); 

  11. var watcher = new CommandWatcher(command); 


  12. command.Executed += Raise.Event(); 


  13. Assert.That(watcher.DidStuff); 



  1. [Test] 

  2. public void MakeSureWatcherSubscribesToCommandExecuted() { 

  3. var command = Substitute.For<ICommand>(); 

  4. var watcher = new CommandWatcher(command); 


  5. // Not recommended. Favour testing behaviour over implementation specifics. 

  6. // Can check subscription: 

  7. command.Received().Executed += watcher.OnExecuted; 

  8. // Or, if the handler is not accessible: 

  9. command.Received().Executed += Arg.Any<EventHandler>(); 



清除调用

  1. public interface ICommand { 

  2. void Execute(); 




  3. public class OnceOffCommandRunner { 

  4. ICommand command; 

  5. public OnceOffCommandRunner(ICommand command) { 

  6. this.command = command; 



  7. public void Run() { 

  8. if (command == null) return; 

  9. command.Execute(); 

  10. command = null; 





  1. var command = Substitute.For<ICommand>(); 

  2. var runner = new OnceOffCommandRunner(command); 


  3. //First run 

  4. runner.Run(); 

  5. command.Received().Execute(); 


  6. //Forget previous calls to command 

  7. //清楚前面的调用 

  8. command.ClearReceivedCalls(); 


  9. //Second run 

  10. runner.Run(); 

  11. command.DidNotReceive().Execute(); 

参数匹配

忽略参数

  1. calculator.Add(Arg.Any<int>(), 5).Returns(7); 


  2. Assert.AreEqual(7, calculator.Add(42, 5)); 

  3. Assert.AreEqual(7, calculator.Add(123, 5)); 

  4. Assert.AreNotEqual(7, calculator.Add(1, 7)); 



  5. formatter.Format(new object()); 

  6. formatter.Format("some string"); 


  7. formatter.Received().Format(Arg.Any<object>()); 

  8. formatter.Received().Format(Arg.Any<string>()); 

  9. formatter.DidNotReceive().Format(Arg.Any<int>()); 

匹配一个参数

  1. calculator.Add(1, -10); 


  2. //Received call with first arg 1 and second arg less than 0: 

  3. calculator.Received().Add(1, Arg.Is<int>(x => x < 0)); 

  4. //Received call with first arg 1 and second arg of -2, -5, or -10: 

  5. calculator 

  6. .Received() 

  7. .Add(1, Arg.Is<int>(x => new[] {-2,-5,-10}.Contains(x))); 

  8. //Did not receive call with first arg greater than 10: 

  9. calculator.DidNotReceive().Add(Arg.Is<int>(x => x > 10), -10); 


  10. formatter.Format(Arg.Is<string>(x => x.Length <= 10)).Returns("matched"); 


  11. Assert.AreEqual("matched", formatter.Format("short")); 

  12. Assert.AreNotEqual("matched", formatter.Format("not matched, too long")); 

  13. // Will not match because trying to access .Length on null will throw an exception when testing 

  14. // our condition. NSubstitute will assume it does not match and swallow the exception. 

  15. Assert.AreNotEqual("matched", formatter.Format(null)); 

匹配特殊参数

  1. calculator.Add(0, 42); 


  2. //This won't work; NSubstitute isn't sure which arg the matcher applies to: 

  3. //calculator.Received().Add(0, Arg.Any<int>()); 


  4. calculator.Received().Add(Arg.Is(0), Arg.Any<int>()); 


使用参数匹配

  1. /* ARRANGE */ 


  2. var widgetFactory = Substitute.For<IWidgetFactory>(); 

  3. var subject = new Sprocket(widgetFactory); 


  4. // OK: Use arg matcher for a return value: 

  5. widgetFactory.Make(Arg.Is<int>(x => x > 10)).Returns(TestWidget); 


  6. /* ACT */ 


  7. // NOT OK: arg matcher used with a real call: 

  8. // subject.StartWithWidget(Arg.Any<int>()); 


  9. // Use a real argument instead: 

  10. subject.StartWithWidget(4); 


  11. /* ASSERT */ 


  12. // OK: Use arg matcher to check a call was received: 

  13. 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. calculator.Add(7,3); 

  7. calculator.Add(2,2); 

  8. calculator.Add(11,-3); 

  9. 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. foo.SayHello("World"); 

  10. foo.SayHello("World"); 

  11. Assert.AreEqual(2, counter); 



复杂调用

  1. var sub = Substitute.For<ISomething>(); 


  2. var calls = new List<string>(); 

  3. var counter = 0; 


  4. sub 

  5. .When(x => x.Something()) 

  6. .Do( 

  7. Callback.First(x => calls.Add("1")) 

  8. .Then(x => calls.Add("2")) 

  9. .Then(x => calls.Add("3")) 

  10. .ThenKeepDoing(x => calls.Add("+")) 

  11. .AndAlways(x => counter++) 

  12. ); 


  13. for (int i = 0; i < 5; i++) 



  14. sub.Something(); 



  15. Assert.That(String.Concat(calls), Is.EqualTo("123++")); 

  16. Assert.That(counter, Is.EqualTo(5)); 

抛出异常

  1. //For non-voids: 

  2. calculator.Add(-1, -1).Returns(x => { throw new Exception(); }); 


  3. //For voids and non-voids: 

  4. calculator 

  5. .When(x => x.Add(-2, -2)) 

  6. .Do(x => { throw new Exception(); }); 


  7. //Both calls will now throw. 

  8. Assert.Throws<Exception>(() => calculator.Add(-1, -1)); 

  9. 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. public class LowFuelWarningEventArgs : EventArgs { 

  6. public int PercentLeft { get; private set; } 

  7. public LowFuelWarningEventArgs(int percentLeft) { 

  8. 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. 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. Assert.AreEqual(2, numberOfEvents); 

代理事件

  1. var sub = Substitute.For<INotifyPropertyChanged>(); 

  2. bool wasCalled = false; 

  3. sub.PropertyChanged += (sender, args) => wasCalled = true; 


  4. sub.PropertyChanged += Raise.Event<PropertyChangedEventHandler>(this, new PropertyChangedEventArgs("test")); 


  5. Assert.That(wasCalled); 

action事件

  1. int revvedAt = 0;; 

  2. engine.RevvedAt += rpm => revvedAt = rpm; 


  3. engine.RevvedAt += Raise.Event<Action<int>>(123); 


  4. 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. //or 

  9. var factory = Substitute.For<INumberParserFactory>(); 

  10. factory.Create(',').Parse("an expression").Returns(new[] {1,2,3}); 

  11. Assert.AreEqual( 

  12. factory.Create(',').Parse("an expression"), 

  13. new[] {1,2,3}); 



  14. var firstCall = factory.Create(','); 

  15. var secondCall = factory.Create(','); 

  16. var thirdCallWithDiffArg = factory.Create('x'); 


  17. Assert.AreSame(firstCall, secondCall); 

  18. 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. //Act 

  11. var result = lookup.TryLookup("hello", out value); 


  12. //Assert 

  13. Assert.True(result); 

  14. Assert.AreEqual(value, "world!"); 

行为和参数匹配

执行回调

  1. public interface IOrderProcessor { 

  2. void ProcessOrder(int orderId, Action<bool> orderProcessed); 




  3. public class OrderPlacedCommand { 

  4. IOrderProcessor orderProcessor; 

  5. IEvents events; 

  6. public OrderPlacedCommand(IOrderProcessor orderProcessor, IEvents events) { 

  7. this.orderProcessor = orderProcessor; 

  8. this.events = events; 



  9. public void Execute(ICart cart) { 

  10. orderProcessor.ProcessOrder( 

  11. cart.OrderId,  

  12. wasOk => { if (wasOk) events.RaiseOrderProcessed(cart.OrderId); } 

  13. ); 





  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. //Act 

  11. var command = new OrderPlacedCommand(processor, events); 

  12. command.Execute(cart); 


  13. //Assert 

  14. events.Received().RaiseOrderProcessed(3); 



用参数执行操作

  1. var argumentUsed = 0; 

  2. calculator.Multiply(Arg.Any<int>(), Arg.Do<int>(x => argumentUsed = x)); 


  3. calculator.Multiply(123, 42); 


  4. Assert.AreEqual(42, argumentUsed); 

  1. var firstArgsBeingMultiplied = new List<int>(); 

  2. calculator.Multiply(Arg.Do<int>(x => firstArgsBeingMultiplied.Add(x)), 10); 


  3. calculator.Multiply(2, 10); 

  4. calculator.Multiply(5, 10); 

  5. calculator.Multiply(7, 4567); //Will not match our Arg.Do as second arg is not 10 


  6. 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. var results = new[] { 

  11. calculator.Multiply(-4, 3), 

  12. calculator.Multiply(-27, 88), 

  13. calculator.Multiply(-7, 8), 

  14. calculator.Multiply(123, 2) //First arg greater than 0, so spec won't be met. 

  15. }; 


  16. Assert.AreEqual(3, numberOfCallsWhereFirstArgIsLessThan0); //3 of 4 calls have first arg < 0 

  17. 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. subject.DoStuff(); 


  7. Received.InOrder(() => { 

  8. connection.Open(); 

  9. command.Run(connection); 

  10. connection.Close(); 

  11. }); 



  1. [Test] 

  2. public void SubscribeToEventBeforeOpeningConnection() { 

  3. var connection = Substitute.For<IConnection>(); 

  4. connection.SomethingHappened += () => { /* some event handler */ }; 

  5. connection.Open(); 


  6. Received.InOrder(() => { 

  7. connection.SomethingHappened += Arg.Any<Action>(); 

  8. connection.Open(); 

  9. }); 



.NET测试--模拟框架NSubstitute的更多相关文章

  1. DotNet 资源大全中文版

    https://blog.csdn.net/fhzh520/article/details/52637545 目录 算法与数据结构(Algorithms and Data structures) 应用 ...

  2. 单元测试模拟框架:Nsubstitute

         Nsubstitute是一个开源的框架,源码是C#实现的.你可以在这里获得它的源码:https://github.com/nsubstitute/NSubstitute NSubstitut ...

  3. .NET Core系列 :4 测试

    2016.6.27 微软已经正式发布了.NET Core 1.0 RTM,但是工具链还是预览版,同样的大量的开源测试库也都是至少发布了Alpha测试版支持.NET Core, 这篇文章 The Sta ...

  4. mock测试框架Mockito

    无论是敏捷开发.持续交付,还是测试驱动开发(TDD)都把单元测试作为实现的基石.随着这些先进的编程开发模式日益深入人心,单元测试如今显得越来越重要了.在敏捷开发.持续交付中要求单元测试一定要快(不能访 ...

  5. .net单元测试——常用测试方式(异常模拟、返回值测试、参数测试、数据库访问代码测试)

    最近在看.net单元测试艺术,我也喜欢单元测试,今天介绍一下如何测试异常.如何测试返回值.如何测试模拟对象的参数传递.如何测试数据库访问代码.单元测试框架使用的是NUnit,模拟框架使用的是:Rhin ...

  6. Android测试:Building Local Unit Tests

    原文:https://developer.android.com/training/testing/unit-testing/local-unit-tests.html 如果你的单元测试没有依赖或者只 ...

  7. Android单元测试之三:使用模拟框架模拟依赖

    Android单元测试之三:使用模拟框架模拟依赖 基本描述 如果是一些工具类方法的测试,如计算两数之和的方法,本地 JVM 虚拟机就能提供足够的运行环境,但如果要测试的单元依赖了 Android 框架 ...

  8. Android单元测试之二:本地测试

    Android单元测试之二:本地测试 本地测试 本地测试( Local tests):只在本地机器 JVM 上运行,以最小化执行时间,这种单元测试不依赖于 Android 框架,或者即使有依赖,也很方 ...

  9. .NET 单元测试的利剑——模拟框架Moq(简述篇)

    .NET 单元测试的利剑--模拟框架Moq 前言 这篇文章是翻译文,因为通过自己参与的项目,越发觉得单元测试的重要性,特别是当跟业务数据打交道的时候的,Moq就如雪中送炭,所以想学习这个框架,就从这篇 ...

随机推荐

  1. JVM之堆体系结构

    1.Heap堆(Java7之前) 一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的.类加载器读取了类文件后,需要把类.方法.常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆 ...

  2. [CF套题] CF-1201

    CF-1201 传送门 # = * A 500 B 1000 C 1500 D 2000 E1 2000 E2 1000 1 (2217) 1672 482 00:09 400 01:40 790 0 ...

  3. HDU4467 Graph【轻重点维护】

    HDU4467 Graph 题意: 给出一张染色图,\(n\)个点每个点是黑色或者白色,\(m\)条带权边,\(q\)次操作,有两种操作: 改变一个点的颜色 问所有边中两个端点的颜色为给定情况的边权和 ...

  4. Educational Codeforces Round 89 (Rated for Div. 2) A. Shovels and Swords (贪心)

    题意:你有\(a\)个树枝和\(b\)个钻石,\(2\)个树枝和\(1\)个钻石能造一个铁铲,\(1\)个树枝和\(2\)个钻石能造一把剑,问最多能造多少铲子和剑. 题解:如果\(a\le b\),若 ...

  5. Codeforces Round #655 (Div. 2) C. Omkar and Baseball (思维)

    题意:有一个数组,每次可以修改子数组,但是修改后每个元素的位置都必须变化,求最少修改多少次使得这个数组有序. 题解:假如这个数组本来就有序,我们直接输出0.否则,对于数组两端,假如它们有序,那么我们可 ...

  6. Chrome Switchs & Chrome Pref

    Chrome Switchs: https://chromium.googlesource.com/chromium/src/+/master/chrome/common/chrome_switche ...

  7. Java魔法堂:调用外部程序

    前言 Java虽然五脏俱全但总有软肋,譬如获取CPU等硬件信息,当然我们可以通过JNI调用C/C++来获取,但对于对C/C++和Windows API不熟的码农是一系列复杂的学习和踩坑过程.那能不能通 ...

  8. k8s-0-集群

    Docker回顾 docker容器封装应用程序好处 内核在3.8以上,才能完整使用docker隔离功能(所有centos6不推荐用) Docker容器化封装应用程序缺点 容器编排工具有哪些 一: K8 ...

  9. 新闻类爬虫库:Newspaper

    newspaper库是一个主要用来提取新闻内容及分析的Python爬虫框架.此库适合抓取新闻网页.操作简单易学,即使对完全没了解过爬虫的初学者也非常的友好,简单学习就能轻易上手,除此之外,使用过程你不 ...

  10. MATLAB中将mat文件转为txt格式文件

    直接保存为txt文件: 可以用fprintf函数,来代替save函数 比如现在我有一个变量a=[0.1223   345.4544] 如果我想保存它的话,可以用下面的程序: fid = fopen(' ...