測試方法
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
}