NSubstitute完全手册(一)入门基础
NSubstitute入门
最简单的入门方式就是创建一个测试项目,并将 NSubstitute 引用到其中。可以通过 NuGet 或 OpenWrap 来获取 NSubstitute 包。也可以直接下载 NSubstitute 文件,然后将 NSubstitute.dll 引用到项目中。
然后就可以创建一个新的测试 Fixture(可以选择使用你最喜欢的UT测试框架,本文涉及的示例中我们使用MSTest),开始思考从哪里入手。
首先,添加 using NSubstitute; 到当前的C#代码文件中,有了它我们就可以开始创建替身了。
现在,比如我们有一个非常简单的计算器接口:
1 public interface ICalculator 2 { 3 int Add(int a, int b); 4 string Mode { get; set; } 5 event EventHandler PoweringUp; 6 }
我们可以让NSubstitute来创建类型实例的替代实例。可以创建诸如 Stub、Mock、Fake、Spy、Test Double 等,但当我们只是想要一个能有一定程度控制的替代实例时,为什么我们要困扰于此呢?
1 [TestMethod] 2 public void Test_GetStarted_GetSubstitute() 3 { 4 ICalculator calculator = Substitute.For<ICalculator>(); 5 }
现在,我们可以告诉被创建的替代实例,当方法被调用时返回一个值:
1 [TestMethod] 2 public void Test_GetStarted_ReturnSpecifiedValue() 3 { 4 ICalculator calculator = Substitute.For<ICalculator>(); 5 calculator.Add(1, 2).Returns(3); 6 7 int actual = calculator.Add(1, 2); 8 Assert.AreEqual<int>(3, actual); 9 }
我们可以检查该替代实例是否接收到了一个指定的调用,或者未收到某指定调用:
1 [TestMethod] 2 public void Test_GetStarted_ReceivedSpecificCall() 3 { 4 ICalculator calculator = Substitute.For<ICalculator>(); 5 calculator.Add(1, 2); 6 7 calculator.Received().Add(1, 2); 8 calculator.DidNotReceive().Add(5, 7); 9 }
如果 Received() 断言失败,NSubstitute 会尝试给出有可能是什么问题:
1 [TestMethod] 2 [ExpectedException(typeof(ReceivedCallsException))] 3 public void Test_GetStarted_DidNotReceivedSpecificCall() 4 { 5 ICalculator calculator = Substitute.For<ICalculator>(); 6 calculator.Add(5, 7); 7 8 calculator.Received().Add(1, 2); 9 }
Expected to receive a call matching: Add(1, 2) Actually received no matching calls. Received 1 non-matching call (non-matching arguments indicated with '*' characters): Add(*5*, *7*)
我们也可以对属性使用与方法类似的 Retures() 语法,或者继续使用简单的属性 setter 来设置返回值。
1 [TestMethod] 2 public void Test_GetStarted_SetPropertyValue() 3 { 4 ICalculator calculator = Substitute.For<ICalculator>(); 5 6 calculator.Mode.Returns("DEC"); 7 Assert.AreEqual<string>("DEC", calculator.Mode); 8 9 calculator.Mode = "HEX"; 10 Assert.AreEqual<string>("HEX", calculator.Mode); 11 }
NSubstitute 支持参数匹配功能,可以设置参数规则,并断言判断是否接收到参数匹配的调用。
1 [TestMethod] 2 public void Test_GetStarted_MatchArguments() 3 { 4 ICalculator calculator = Substitute.For<ICalculator>(); 5 6 calculator.Add(10, -5); 7 8 calculator.Received().Add(10, Arg.Any<int>()); 9 calculator.Received().Add(10, Arg.Is<int>(x => x < 0)); 10 }
我们也可以在使用参数匹配功能的同时,传递一个函数给 Returns() ,以此来使替代实例具有更多的功能。
1 [TestMethod] 2 public void Test_GetStarted_PassFuncToReturns() 3 { 4 ICalculator calculator = Substitute.For<ICalculator>(); 5 calculator 6 .Add(Arg.Any<int>(), Arg.Any<int>()) 7 .Returns(x => (int)x[0] + (int)x[1]); 8 9 int actual = calculator.Add(5, 10); 10 11 Assert.AreEqual<int>(15, actual); 12 }
Returns() 也可通过构造一个返回值序列来指定多个参数。
1 [TestMethod] 2 public void Test_GetStarted_MultipleValues() 3 { 4 ICalculator calculator = Substitute.For<ICalculator>(); 5 calculator.Mode.Returns("HEX", "DEC", "BIN"); 6 7 Assert.AreEqual<string>("HEX", calculator.Mode); 8 Assert.AreEqual<string>("DEC", calculator.Mode); 9 Assert.AreEqual<string>("BIN", calculator.Mode); 10 }
最后,我们可以在替代实例上引发事件通知:
1 [TestMethod] 2 public void Test_GetStarted_RaiseEvents() 3 { 4 ICalculator calculator = Substitute.For<ICalculator>(); 5 bool eventWasRaised = false; 6 7 calculator.PoweringUp += (sender, args) => 8 { 9 eventWasRaised = true; 10 }; 11 12 calculator.PoweringUp += Raise.Event(); 13 14 Assert.IsTrue(eventWasRaised); 15 }
基本上这些就是入门 NSubstitute 的全部内容。对于更详细的功能说明,以及一些 NSubstitute 所支持的不太常用的需求,请继续阅读。
STSdb,最强纯C#开源NoSQL和虚拟文件系统
回顾
几个月前写了一篇文章,关于 C#写的NoSQL开源项目/系统(系列),看过该文章的同学,估计会对.NET能实现高效的NoSQL是有信心的。几个月过去了,有了新进展。
STSdb是什么
再来说明一下STSdb是什么:STSdb是C#写的开源数据库和虚拟文件系统,支持实时索引,性能是同类产品的几倍到几十倍,访问官方网站。
特性
- 支持几十亿级别的数据存取
- 支持TB级别文件大小
- 实时索引
- 内置压缩
- 内置序列化
- 支持稀疏分散的文件(byte[])
为什么?
数据库的共同点
每个数据库都有一个共同点,就是受限于索引结构,因此每当改进了索引数据结构,性能也随之大增。
性能瓶颈
在数据库系统中,每当对数据进行索引,数据的逻辑位置会被映射到物理设备,这个映射严重依赖设备的寻址时间。
解决办法
当遇到性能瓶颈时:
办法就是,不对每个操作都进行一次寻址,而是每次寻址都做多个操作。
解决方案-WATERFALLTREE™ (瀑布树)
STSdb 4.0突破性地发现并实现了Waterfall-tree (瀑布树)树结构。Waterfall-tree是注册商标,并且拥有专利。与传统的B+树类似,但B+树是同步操作,而瀑布树是分组异步操作。
现有的解决方案有一个共同的缺点:当索引随机主键的时候,I/O性能不高(退化严重)。
而STSdb4.0的瀑布树带了来前所未有的性能提升。
下图直观地展示了瀑布树的原理:
性能比较
数据库 | 存储设计 |
---|---|
Amazon Dynamo DB | Zero-Hop Distributed Hash Table |
Cassandra | Memtable / SSTable |
Couch DB | Append-only B-tree |
Db4objects | B-tree |
Google Big Table | SSTable (log-structured storage) |
H base | Memtable / SSTable on HDFS |
Level DB | LSM-tree |
Memcached | Memory caching |
Mongo DB | B-tree |
Oracle Berkeley DB | B+-tree |
Perst | B-tree/T-tree/R-tree/Patricia trie/KD-tree |
Redis | In-memory only with background snapshots |
Scalaris | In-memory only |
Tokyo Cabinet | Hash or B-tree |
随机写
随机读
顺序写
顺序读
这个性能,只能用凶残来形容啊!
应用领域
Hello STSdb
打开数据库并写入数据
using (StorageEngine engine = new StorageEngine( "stsdb4.sys" , "stsdb4.dat" )) { XIndex< int , string > table = engine.OpenXIndex< int , string >( "table" ); for ( int i = 0; i < 1000000; i++) { table[i] = i.ToString(); } table.Flush(); engine.Commit(); } |
读取数据
using (StorageEngine engine = new StorageEngine( "stsdb4.sys" , "stsdb4.dat" )) { XIndex< int , string > table = engine.OpenXIndex< int , string >( "table" ); foreach (var row in table) //table.Forward(), table.Backward() { Console.WriteLine( "{0} {1}" , row.Key, row.Value); } } |