[C#.NET] 單元測試 - 如何使用 NSubstitute 模擬被測物件與其它物件互動

[C#.NET] 單元測試 - 如何使用 NSubstitute 模擬被測物件與其它物件互動

很多時候被測目標沒有回傳值(viod 方法),若想要驗証被測方法是否有做了哪些動作(調用方法),Received() 擴充方法加上被測方法,

Note:

有關 NSubstitue 的用法:

  • 模擬物件 ICalculator 實體化:Substitute.For<ICalculator>()
  • 驗証被測目標有調用某方法1次:.Received(1)
  • 驗証被測目標無調用某方法:.DidNotReceive()
  • 驗証被測目標有調用某方法1次,忽略參數驗証:.ReceivedWithAnyArgs(1)
  • 驗証被測目標無調用某方法,忽略參數驗驗証:.DidNotReceiveWithAnyArgs()

 

有關隔離技巧 Stub、Mock、Fake:

下圖出自:http://www.dotblogs.com.tw/hatelove/2012/11/29/learning-tdd-in-30-days-day7-unit-testing-stub-mock-and-fake-object-introduction

 

準備 ICalculator 和 Command

public interface ICalculator 
{
    int Add(int first, int senond);
    int Subtract(int first, int senond);
    event EventHandler Executed;
    string Mode { get; set; }
}

public class Command
{
    private ICalculator calculator;
    public bool Called { get; set; }
    public Command(ICalculator calculator)
    {
        this.calculator = calculator;
        calculator.Executed += command_Executed;
    }

    void command_Executed(object sender, EventArgs e)
    {
        this.Called = true;
    }

    public void Do()
    {
        calculator.Add(1, 3);
        if (string.IsNullOrWhiteSpace(calculator.Mode))
        {
            calculator.Mode = "Add";
        }
    }

    public void NotDo()
    {

    }
} 

驗証是否有調用某方法幾次並忽略參數範圍:

public void CheckCallReceivedCount_Test()
{
    //Arrange
    var mock = Substitute.For<ICalculator>();
    var command = new Command(mock);

    //Act
    command.Do();

    //Assert
    mock.Received(1).Add(Arg.Any<int>(), Arg.Any<int>());
}

 

驗証是否有調用某方法幾次並驗証參數範圍:

public void CheckCallArgAndReceivedCount_Test()
{
    //Arrange
    var mock = Substitute.For<ICalculator>();
    var command = new Command(mock);

    //Act
    command.Do();

    //Assert
    mock.Received(1).Add(Arg.Is(1), Arg.Is<int>(x => x > 2));
}

 

驗証是否沒調用某方法並忽略參數範圍:

  • DidNotReceive()方法用來驗証是否無調用
public void CheckNoCallReceivedCount_Test()
{
    //Arrange
    var mock = Substitute.For<ICalculator>();
    var command = new Command(mock);

    //Act
    command.NotDo();

    //Assert
    mock.DidNotReceive().Add(Arg.Any<int>(), Arg.Any<int>());
}

 

忽略參數:

  • 忽略參數,驗証是否有調用某方法: .ReceivedWithAnyArgs(),這跟 Received(1).Add(Arg.Any<int>(), Arg.Any<int>()) 一樣
  • 忽略參數,驗証是否無調用某方法: .DidNotReceiveWithAnyArgs(),這跟mock.DidNotReceive().Add(Arg.Any<int>(), Arg.Any<int>())一樣
public void Test_CheckReceivedCalls_IgnoringArguments()
{
    //Arrange
    var mock = Substitute.For<ICalculator>();
    var command = new Command(mock);

    //Act
    command.Do();

    //Assert
    mock.ReceivedWithAnyArgs().Add(2, 2);
    mock.DidNotReceiveWithAnyArgs().Subtract(1, 3);
} 

 

驗証觸發事件後的結果:

  • Raise.Event()方法,用來引發事件

 

public void CheckEventSubscription_Test()
{
    //Arrange
    var mock = Substitute.For<ICalculator>();
    var command = new Command(mock);

    //Act
    mock.Executed += Raise.Event();

    //Assert
    Assert.IsTrue(command.Called);
} 

 

驗証事件是否有被觸發:

  • 不建議使用此方法,應該是測事件引發後的結果,而不是事件的實現方式
public void CheckCallEventSubscription_Test() 
{    
	//Arrange   
	var mock = Substitute.For<ICalculator>();    
	var command = new Command(mock);    
	//assert    
	mock.Received().Executed += command.OnExecuted;    
	mock.Received().Executed += Arg.Any<EventHandler>(); 
}

 

驗証是否有存取屬性:

  • 驗証是否調用屬性的 getter:var temp = mock.Received().Mode,需要特地指派給一個臨時變數
  • 驗証是否調用屬性的 setter,參數為Add:mock.Received().Mode = "Add";
public void CheckCallPropety_Test()
{
    //Arrange
    var mock = Substitute.For<ICalculator>();
    var command = new Command(mock);

    //Act
    command.Do();

    //驗証是否調用屬性的getter
    var temp = mock.Received().Mode;

    //驗証是否調用屬性的setter,參數為Add
    mock.Received().Mode = "Add";
}

 

驗証索引器:

public void Test_CheckReceivedCalls_CheckingCallsToIndexers()
{
    var dictionary = Substitute.For<IDictionary<string, int>>();
    dictionary["test"] = 1;

    dictionary.Received()["test"] = 1;
    dictionary.Received()["test"] = Arg.Is<int>(x => x < 2);
} 

 

文章出自:https://www.dotblogs.com.tw/yc421206/2015/02/17/149512

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo