前言
上一篇提到了 NSubstitute,前輩看到後提點了一些用法,順勢研究完多一篇筆記紀錄一下(絕對不是我原本想要跳過不介紹被抓到才補上這篇)。
本文
驗證互動是否符合預期方法
接續上次使用範例。
情境:
我們現在有一個 Check 的方法,若出現錯誤則會紀錄一筆log。
首先先把 log 方法準備好。
public interface ILog
{
void SaveLog(string message);
}
public class Log : ILog
{
public void SaveLog(string message)
{
Console.WriteLine(message);
}
}
Check 方法。
bool Check(string text);
public bool Check(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
this._log.SaveLog("text 不可為空!");
return false;
}
return true;
}
接下來我們先調整一下我們的測試方法。
private ILog _log;
private IRepository _repository;
[TestInitialize]
public void TestInitialize()
{
this._log = Substitute.For<ILog>();
this._repository = Substitute.For<IRepository>();
}
private Service GetSystemUnderTest()
{
var sut = new Service(this._repository,this._log);
return sut;
}
那我們測試方法就可以這麼做。
[TestMethod()]
[Owner("Sian")]
[TestCategory("ServiceTest")]
[TestProperty("ServiceTests", "Check")]
public void log_是否有被呼叫()
{
//arrange
var sut = GetSystemUnderTest();
//act
var actual = sut.Check(string.Empty);
//assert
actual.Should().BeFalse();
this._log.Received(1);
}
也可以驗證 log 記下的內容。
[TestMethod()]
[Owner("Sian")]
[TestCategory("ServiceTest")]
[TestProperty("ServiceTests", "Check")]
public void log_有被呼叫_應記下正確內容()
{
//arrange
var sut = GetSystemUnderTest();
//act
var actual = sut.Check(string.Empty);
//assert
actual.Should().BeFalse();
this._log.Received(1).SaveLog(Arg.Is<string>(x => x.Contains("text")));
}
若不應該收到訊息可以這麼測試。
[TestMethod()]
[Owner("Sian")]
[TestCategory("ServiceTest")]
[TestProperty("ServiceTests", "Check")]
public void log_沒有被呼叫()
{
//arrange
var sut = GetSystemUnderTest();
//act
var actual = sut.Check("123");
//assert
actual.Should().BeTrue();
this._log.DidNotReceive();
}
傳遞的參數
情境:
Insert 一筆資料到 DB 若成功回傳 True,失敗則 False。
Repository 準備
/// <summary>
/// 新增帳號
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
bool InsertAccount(AccountModel model);
public bool InsertAccount(AccountModel model)
{
//你知道的這只是一種假設沒有要真的實作。
return true;
}
Service 的部分。
/// <summary>
///
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
bool InserAccount(AccountModel model);
public bool InserAccount(AccountModel model)
{
var result = this._repository.InsertAccount(model);
return result;
}
那我們的測試就可以這麼做。
[TestMethod()]
[Owner("Sian")]
[TestCategory("ServiceTest")]
[TestProperty("ServiceTests", "InsertAccount")]
public void InsertAccount_成功_應回傳true()
{
//arrange
var data = new AccountModel
{
Account = "123",
UserName = "Sian"
};
var sut = GetSystemUnderTest();
this._repository.InsertAccount(Arg.Any<AccountModel>()).Returns(true);
//act
var actual = sut.InserAccount(data);
//assert
actual.Should().BeTrue();
}
可以看到傳遞給 Repository 的參數我們直接使用了 Args.Any 這個方式,並直接指定了回傳的內容,又被你學到一個偷懶的方式 。
同樣的驗證False也可以這麼做。
[TestMethod()]
[Owner("Sian")]
[TestCategory("ServiceTest")]
[TestProperty("ServiceTests", "InsertAccount")]
public void InsertAccount_失敗_應回傳False()
{
//arrange
var data = new AccountModel
{
Account = "123",
UserName = "Sian"
};
var sut = GetSystemUnderTest();
this._repository.InsertAccount(Arg.Any<AccountModel>()).Returns(false);
//actr
var actual = sut.InserAccount(data);
//assert
actual.Should().BeFalse();
}
驗證執行的次數
情境:
有一個內有迴圈的方法。
public interface ICommand
{
void Execute();
}
public class Command
{
ICommand _command;
int _numberOfTimesToCall;
public Command(
ICommand command,
int numberOfTimesToCall)
{
this._command = command;
this._numberOfTimesToCall = numberOfTimesToCall;
}
public void Execute()
{
for (var i = 0; i < this._numberOfTimesToCall; i++)
{
this._command.Execute();
}
}
}
那我們測試就可以這麼做
private ICommand _command;
private int _numberOfTimesToCall;
[TestInitialize]
public void TestInitialize()
{
this._command = Substitute.For<ICommand>();
}
private Command GetSystemUnderTest()
{
var sut = new Command(this._command, this._numberOfTimesToCall);
return sut;
}
[TestMethod()]
[Owner("Sian")]
[TestCategory("CommandTests")]
[TestProperty("CommandTests", "Execute")]
public void Execute_被執行了3次_正確計算應為3()
{
//arrange
this._numberOfTimesToCall = 3;
var sut = GetSystemUnderTest();
//Act
sut.Execute();
//Assert
this._command.Received(3).Execute();
}
不為3則會報錯。
[TestMethod()]
[Owner("Sian")]
[TestCategory("CommandTests")]
[TestProperty("CommandTests", "Execute")]
public void Execute_被執行了3次_正確計算應為3()
{
//arrange
this._numberOfTimesToCall = 4;
var sut = GetSystemUnderTest();
//Act
sut.Execute();
//Assert
this._command.Received(3).Execute();
}
範例連結
這幾次示範範例連結會放在以下網址,歡迎取用。