為什麼需要做測試?一個沒有經過測試或驗證的程式碼,都是💩(開玩笑)ㄈ
利用測試驅動開發,先撰寫測試在撰寫程式碼的方式,除了可以確保程式碼是可測的之外,他也能保證在測試案例範圍內是不會有錯的,也因此也能利用測試文件作為規格書使用。
利用unity內建的test runner製作單元測試可測unity兩種環境,Play Mode和Editor Mode,我將會分別介紹如何撰寫兩種測試。
須在Editor資料夾中建立測試腳本
撰寫測試程式碼
public class TestScript
{
[Test]
[TestCase(10,'+',2,12)]
[TestCase(10, '-', 2, 8)]
[TestCase(10, '*', 2, 20)]
[TestCase(500, '/', 10, 50)]
public void Calculate_Test(float x, char pointer, float y,float result)
{
var Calculator = new Calculator(x,y,pointer);
var score = Calculator.ScoreCount();
Assert.That(score,Is.EqualTo(result));
}
}
public class Calculator
{
float x;
float y;
char pointer;
public Calculator(float x,float y,char pointer)
{
this.x = x;
this.y = y;
this.pointer = pointer;
}
public float ScoreCount()
{
float _value = 0;
switch (pointer)
{
case '+':
_value = x + y;
break;
case '-':
_value = x - y;
break;
case '*':
_value = x * y;
break;
case '/':
_value = (x / y);
break;
default:
break;
}
return _value;
}
}
跑Unity Test Runner
利用Test Runner建立PlayMode測試資料夾
撰寫測試程式碼
[UnityTest]
public IEnumerator SceneLoading(){
//store test scene
Scene currentScene = SceneManager.GetActiveScene();
//load scene
yield return SceneManager.LoadSceneAsync("testScene",LoadSceneMode.Additive);
//After it is loaded,set the secne as Active
SceneManager.SetActiveScene(SceneManager.GetSceneByName("testScene"));
//Assert that the game scene has been set to active.
Assert.IsTrue(SceneManager.GetActiveScene().name == "testScene");
//Clean up
SceneManager.SetActiveScene(currentScene);
yield return SceneManager.UnloadSceneAsync("testScene");
}
跑Unity Test Runner
NSubstitute為Mocking Object,可以模擬DOC製作出一個假物件進行測試。需注意的是,必須有Virtual才能做替換
一個SUT
public interface ICalculator
{
int Add(int x,int y);
string Mode{get;set;}
event EventHandler AnEventAction;
}
示範如何自訂輸出SUT資料
[Test]
public void NSubstitute_Test(){
//Arrange
ICalculator calculator = Substitute.For<ICalculator>();
//Act
calculator.Add(1,2).Returns(3);
//Assert
Assert.That(calculator.Add(1,2),Is.EqualTo(3));
}
結果:
自訂SUT輸入參數
[Test]
public void NSubstitute_Test(){
//Arrange
ICalculator calculator = Substitute.For<ICalculator>();
//Act
calculator.Add(1,2);
//Assert
calculator.Received().Add(1,2);
calculator.DidNotReceive().Add(5,7);
}
結果:
失敗案例
[Test]
public void NSubstitute_Method_Test(){
//Arrange
ICalculator calculator = Substitute.For<ICalculator>();
//Act
calculator.Add(5,7);
//Assert
calculator.Received().Add(1,2);
calculator.DidNotReceive().Add(5,7);
}
結果:
自訂屬性,可用兩種方式定義
[Test]
public void NSubstitute_Property_Test(){
//1.
//Arrange
ICalculator calculator = Substitute.For<ICalculator>();
//Act
calculator.Mode.Returns("Decimal");
//Assert
Assert.That(calculator.Mode == "Decimal");
//2.
//Act
calculator.Mode ="Hex";
//Assert
Assert.That(calculator.Mode== "Hex");
}
結果
支援參數檢查
[Test]
public void NSubstitute_ArgsCheck_Test(){
//1.
//Arrange
ICalculator calculator = Substitute.For<ICalculator>();
//Act
calculator.Add(1,5);
//Assert
calculator.Received().Add(1,Arg.Is<int>(x=>x<6));
}
結果:
失敗案例
[Test]
public void NSubstitute_ArgsCheck_Test(){
//1.
//Arrange
ICalculator calculator = Substitute.For<ICalculator>();
//Act
calculator.Add(1,10);
//Assert
calculator.Received().Add(1,Arg.Is<int>(x=>x<6));
}
結果:
在參數做判斷(炫技寫法)
[Test]
public void NSubstitute_ArgsCheck_Test(){
//Arrange
ICalculator calculator = Substitute.For<ICalculator>();
//Act
calculator.Add(Arg.Is<int>((x)=>x>0),Arg.Is<int>((x)=>x>0))
.Returns(x=>(int)x[0]+(int)x[1]);
//Assert
Assert.That(calculator.Add(5,10) == 15);
}
結果:
Return可一次定義多回傳值
回傳值具有順序性
[Test]
public void NSubstitute_Return_Test(){
//Arrange
ICalculator calculator = Substitute.For<ICalculator>();
//Act
calculator.Mode.Returns("Hex","Dec","Bin");
//Assert
Assert.That(calculator.Mode=="Hex");
Assert.That(calculator.Mode=="Dec");
Assert.That(calculator.Mode=="Bin");
}
結果:
若對調順序
[Test]
public void NSubstitute_Return_Test(){
//Arrange
ICalculator calculator = Substitute.For<ICalculator>();
//Act
calculator.Mode.Returns("Hex","Dec","Bin");
//Assert
Assert.That(calculator.Mode=="Bin");
Assert.That(calculator.Mode=="Hex");
Assert.That(calculator.Mode=="Dec");
}
結果:
事件測試
[Test]
public void NSubstitute_Event_Test(){
//Arrange
ICalculator calculator = Substitute.For<ICalculator>();
bool isEventTrigger = false;
//Act
calculator.AnEventAction += (sender,args)=>{isEventTrigger = true;};
calculator.AnEventAction += Raise.Event();
//Assert
Assert.That(isEventTrigger);
}
結果:
待續…
泰迪
肉豬
Nsubstitute Github
Unit testing MonoBehaviours(UnityBlog)
Unity Test Framework
Performance Benchmarking in Unity