测试方法
1
//
准备 Mock IFoo 接口
2
var mock
=
new
Mock
<
IFoo
>
();
3
//
配置准备模拟的方法,当调用接口中的 DoSomething 方法,并传递参数 "bing" 的时候,返回 true
4
mock.Setup(foo
=>
foo.DoSomething(
"
ping
"
)).Returns(
true
);
5
6
//
方法的参数中使用了 out 参数
7
//
out arguments
8
var outString
=
"
ack
"
;
9
//
当调用 TryParse 方法的时候,out 参数返回 "ack", 方法返回 true, lazy evaluated
10
mock.Setup(foo
=>
foo.TryParse(
"
ping
"
,
out
outString)).Returns(
true
);
11
12
//
ref 参数
13
var instance
=
new
Bar();
14
//
仅仅在使用 ref 调用的时候,才会匹配下面的测试
15
mock.Setup(foo
=>
foo.Submit(
ref
instance)).Returns(
true
);
16
17
//
当方法返回值得时候,还能够访问返回的值
18
//
这里能够使用多个参数
19
mock.Setup(x
=>
x.DoSomething(It.IsAny
<
string
>
()))
20
.Returns((
string
s)
=>
s.ToLower());
21
22
//
在被调用的时候抛出异常
23
mock.Setup(foo
=>
foo.DoSomething(
"
reset
"
)).Throws
<
InvalidOperationException
>
();
24
mock.Setup(foo
=>
foo.DoSomething(
""
)).Throws(
new
ArgumentException(
"
command
"
);
25
26
//
延迟计算返回的结果
27
mock.Setup(foo
=>
foo.GetCount()).Returns(()
=>
count);
28
29
//
在每一次调用的时候,返回不一样的值
30
var mock
=
new
Mock
<
IFoo
>
();
31
var calls
=
0
;
32
mock.Setup(foo
=>
foo.GetCountThing())
33
.Returns(()
=>
calls)
34
.Callback(()
=>
calls
++
);
35
36
//
第一次调用返回 0, 下一次是 1, 依次类推
37
Console.WriteLine(mock.Object.GetCountThing());
匹配参数
1
//
任意值
2
mock.Setup(foo
=>
foo.DoSomething(It.IsAny
<
string
>
())).Returns(
true
);
3
4
//
提供的值必须匹配一个函数, lazy evaluated
5
mock.Setup(foo
=>
foo.Add(It.Is
<
int
>
(i
=>
i
%
2
==
0
))).Returns(
true
);
6
7
//
匹配一个范围
8
mock.Setup(foo
=>
foo.Add(It.IsInRange
<
int
>
(
0
,
10
, Range.Inclusive))).Returns(
true
);
9
10
//
匹配正则表达式
11
mock.Setup(x
=>
x.DoSomething(It.IsRegex(
"
[a-d]+
"
, RegexOptions.IgnoreCase))).Returns(
"
foo
"
);
属性
1
//
普通属性
2
mock.Setup(foo
=>
foo.Name).Returns(
"
bar
"
);
3
4
//
多层的属性
5
mock.Setup(foo
=>
foo.Bar.Baz.Name).Returns(
"
baz
"
);
6
7
//
指望设置属性的值为 "foo"
8
mock.SetupSet(foo
=>
foo.Name
=
"
foo
"
);
9
10
//
或者直接验证赋值
11
mock.VerifySet(foo
=>
foo.Name
=
"
foo
"
);
设置属性,以便自动跟踪它的值正则表达式
1
//
开始 "tracking" 属性的 sets/gets
2
mock.SetupProperty(f
=>
f.Name);
3
4
//
提供一个默认的值
5
mock.SetupProperty(f
=>
f.Name,
"
foo
"
);
6
7
//
如今,你能够:
8
IFoo foo
=
mock.Object;
9
10
//
保存的值
11
Assert.Equal(
"
foo
"
, foo.Name);
12
13
//
从新设置一个值
14
foo.Name
=
"
bar
"
;
15
Assert.Equal(
"
bar
"
, foo.Name);
还能够准备全部的属性数组
mock.SetupAllProperties();
事件
1
//
抛出一个事件
2
mock.Raise(m
=>
m.FooEvent
+=
null
,
new
FooEventArgs(fooValue));
3
4
//
多层的后代中的事件
5
mock.Raise(m
=>
m.Child.First.FooEvent
+=
null
,
new
FooEventArgs(fooValue));
6
7
//
当 Submit 方法被调用的时候,抛出一个事件
8
mock.Setup(foo
=>
foo.Submit()).Raises(f
=>
f.Sent
+=
null
, EventArgs.Empty);
9
10
//
抛出异常将会触发对象底层的行为
11
//
你可能须要在后面进行断言处理
12
13
//
抛出一个自定义的事件
14
public
delegate
void
MyEventHandler(
int
i,
bool
b);
15
public
interface
IFoo {
event
MyEventHandler MyEvent; }
16
var mock
=
new
Mock
<
IFoo
>
();
17
...
18
19
//
传递自定义的事件参数
20
mock.Raise(foo
=>
foo.MyEvent
+=
null
,
25
,
true
);
回调
1
var mock
=
new
Mock
<
IFoo
>
();
2
mock.Setup(foo
=>
foo.Execute(
"
ping
"
))
3
.Returns(
true
)
4
.Callback(()
=>
calls
++
);
5
6
//
使用调用的参数
7
mock.Setup(foo
=>
foo.Execute(It.IsAny
<
string
>
()))
8
.Returns(
true
)
9
.Callback((
string
s)
=>
calls.Add(s));
10
11
//
使用泛型语法
12
mock.Setup(foo
=>
foo.Execute(It.IsAny
<
string
>
()))
13
.Returns(
true
)
14
.Callback
<
string
>
(s
=>
calls.Add(s));
15
16
//
使用多个参数
17
mock.Setup(foo
=>
foo.Execute(It.IsAny
<
int
>
(), It.IsAny
<
string
>
()))
18
.Returns(
true
)
19
.Callback
<
int
,
string
>
((i, s)
=>
calls.Add(s));
20
21
//
调用以前和以后的回调
22
mock.Setup(foo
=>
foo.Execute(
"
ping
"
))
23
.Callback(()
=>
Console.WriteLine(
"
Before returns
"
))
24
.Returns(
true
)
25
.Callback(()
=>
Console.WriteLine(
"
After returns
"
));
验证
1
mock.Verify(foo
=>
foo.Execute(
"
ping
"
));
2
3
//
在验证失败的时候,提供自定义的错误提示信息
4
mock.Verify(foo
=>
foo.Execute(
"
ping
"
),
"
When doing operation X, the service should be pinged always
"
);
5
6
//
从没有被调用的方法
7
mock.Verify(foo
=>
foo.Execute(
"
ping
"
), Times.Never());
8
9
//
至少调用过一次
10
mock.Verify(foo
=>
foo.Execute(
"
ping
"
), Times.AtLeastOnce());
11
mock.VerifyGet(foo
=>
foo.Name);
12
13
//
验证对属性的赋值.
14
mock.VerifySet(foo
=>
foo.Name);
15
16
//
验证对于属性设置特定的值
17
mock.VerifySet(foo
=>
foo.Name
=
"
foo
"
);
18
19
//
验证匹配的参数
20
mock.VerifySet(foo
=>
foo.Value
=
It.IsInRange(
1
,
5
, Range.Inclusive));
自定义 Mock 行为
Mock 的行为分为严格的 Strict 和宽松的 Loose, 默认为宽松的。在严格模式下,使用任何没有被指定的行为,都将会抛出异常,宽松模式下,不会抛出任何异常,方法将会返回默认值或者空的数组等等。函数
var mock
=
new
Mock
<
IFoo
>
(MockBehavior.Strict);
若是没有重写基类的实现,默认将不会调用基类,在 Mock Web/Html 控件的是必须的。测试
var mock
=
new
Mock
<
IFoo
>
{ CallBase
=
true
};
创造自动递归的 Mock, Mock 对象对于它的任何成员将会返回一个新的 Mock 对象。lua
var mock
=
new
Mock
<
IFoo
>
{ DefaultValue
=
DefaultValue.Mock };
//
默认是 DefaultValue.Empty
//
如今这个属性将会返回一个新的 Mock 对象
IBar value
=
mock.Object.Bar;
//
能够使用返回的 Mock 对象, 后即对属性的访问返回相同的对象实例
//
这就容许咱们能够进行后继的设置
//
set further expectations on it if we want
var barMock
=
Mock.Get(value);
barMock.Setup(b
=>
b.Submit()).Returns(
true
);
中心化的 Mock 实例建立和管理:你能够在一个地方使用 MockRepository 建立和验证全部的 Mock 对象,设置 MockBehavior, CallBse 和 DefaultValue 约束。spa
var factory
=
new
MockFactory(MockBehavior.Strict) { DefaultValue
=
DefaultValue.Mock };
//
建立 Mock 对象
var fooMock
=
factory.Create
<
IFoo
>
();
//
在建立的时候重写仓库的设置
var barMock
=
factory.Create
<
IBar
>
(MockBehavior.Loose);
//
验证经过仓库建立的对象
factory.Verify();
其它
//
用在测试用例的开始
using
Moq.Protected()
//
测试中
var mock
=
new
Mock
<
CommandBase
>
(); mock.Protected()
.Setup
<
int
>
(
"
Execute
"
)
.Returns(
5
);
//
若是用到了参数匹配, 必须使用 ItExpr 来代替 It
//
之后计划改进
mock.Protected()
.Setup
<
string
>
(
"
Execute
"
, ItExpr.IsAny
<
string
>
())
.Returns(
true
);
高级特性
1
//
从 Mock 实例从新得到 Mock 对象
2
IFoo foo
=
//
get mock instance somehow
3
var fooMock
=
Mock.Get(foo);
4
fooMock.Setup(f
=>
f.Submit()).Returns(
true
);
5
6
//
实现多个接口
7
var foo
=
new
Mock
<
IFoo
>
();
8
var disposableFoo
=
foo.As
<
IDisposable
>
();
9
10
//
如今 IFoo mock 已经实现了接口 IDisposable :) disposableFoo.Setup(df => df.Dispose());
11
12
//
定制匹配
13
mock.Setup(foo
=>
foo.Submit(IsLarge())).Throws
<
ArgumentException
>
(); ...
14
public
string
IsLarge()
15
{
16
return
Match
<
string
>
.Create(s
=>
!
String.IsNullOrEmpty(s)
&&
s.Length
>
100
);
17
}