昨天已經把 DemoCalculate 中的 Calculate 方法檢驗參數的部分抽離出來,寫成一隻隻的個別方法,這樣的好處是可以為這些方法單獨寫測試,若而後要修改這些方法時,彼此之間是互不影響,所以找問題相較緊耦合的第一版,第二版的寫法更符合 SOLID 原則。不過若要再進一步探討的話,還可以持續精進程式碼,所以今天就來探討如何更近一步精進。
那一樣,我們先來撰寫更新一版的測試碼,如下:
public class DemoCalculateTests_Ver3
{
[Test]
public void NormalCaseTest()
{
// 與 Day-28 一樣
}
[Test]
public void FailCaseTest_MathIsNotNumeric()
{
// 與 Day-28 一樣
}
[Test]
public void FailCaseTest_MathIsInZeroToTen()
{
// 與 Day-28 一樣
}
[Test]
public void AvgCaseTest_Country()
{
// Arrange
string txtID = "Day-29";
string txtName = "SunShineYen";
string txtMath = "8";
string txtLiteral = "4";
string isCountry = "true";
DemoCalculate_Ver2 testObject
= new DemoCalculate_Ver2(txtID, txtName, txtMath, txtLiteral, isCountry);
// Act
string result = testObject.Calculate();
// Assert
Assert.AreEqual(result, "Day-29 SunShineYen 6.5");
}
[Test]
public void AvgCaseTest_City()
{
// Arrange
string txtID = "Day-29";
string txtName = "SunShineYen";
string txtMath = "8";
string txtLiteral = "4";
string isCountry = "false";
DemoCalculate_Ver2 testObject
= new DemoCalculate_Ver2(txtID, txtName, txtMath, txtLiteral, isCountry);
// Act
string result = testObject.Calculate();
// Assert
Assert.AreEqual(result, "Day-29 SunShineYen 6");
}
}
這當中我們新增了檢驗平均值是否符合預期,基於平均的回傳是一串字串,所以我們也撰寫相對應的字串做驗證。
這當中是可以修改方法以利擴充,因 Calculate 是公開的方法,因此不論是檢驗參數,或驗證平均都需要經過 Calculate 方法,但倘若全部公開,則一些極機密的方法就會被公開,所以可撰寫針對檢驗參數與驗證平均分別寫獨立的方法,這樣不但可以做測試,也可以再擴充其他方法時使用,如下:
public class DemoCalculate_Ver3
{
private string txtID;
private string txtName;
private string txtMath;
private string txtLiteral;
private string isCountry;
public DemoCalculate_Ver3(string inTxtID, string inTxtName,
string inTxtMath, string inTxtLiteral, string inIsCountry)
{
txtID = inTxtID;
txtName = inTxtName;
txtMath = inTxtMath;
txtLiteral = inTxtLiteral;
isCountry = inIsCountry;
}
public string Calculate()
{
if (CheckInitPara() != "沒錯誤")
{
return CheckInitPara();
}
return StudentAvg();
}
// 檢驗參數
public string CheckInitPara()
{
if (MathInputValid() != "沒錯誤")
{
return MathInputValid();
}
if (LiteralInputValid() != "沒錯誤")
{
return LiteralInputValid();
}
return "沒錯誤";
}
private string MathInputValid()
{
double result;
if (!double.TryParse(txtMath, out result))
{
return "發生錯誤!數學數值應該是個 numeric!";
}
if (((Convert.ToDouble(txtMath) > 10)) || (Convert.ToDouble(txtMath) < 0))
{
return "發生錯誤!數學數值應該介於 0 ~ 10 之間!";
}
return "沒錯誤";
}
private string LiteralInputValid()
{
double result;
if (!double.TryParse(txtLiteral, out result))
{
return "發生錯誤!文學數值應該是個 numeric!";
}
if (((Convert.ToDouble(txtLiteral) > 10)) || (Convert.ToDouble(txtLiteral) < 0))
{
return "發生錯誤!文學數值應該介於 0 ~ 10 之間!";
}
return "沒錯誤";
}
// 計算數值
public string StudentAvg()
{
double douMath = Convert.ToDouble(txtMath);
double douLite = Convert.ToDouble(txtLiteral);
if (Convert.ToBoolean(isCountry))
{
return txtID + " " + txtName + " "
+ CountryAvg(douMath, douLite).ToString();
}
else
{
return txtID + " " + txtName + " " + CityAvg(douMath, douLite).ToString();
}
}
private double CountryAvg(double dbMath, double dbLiteral)
{
return (dbMath + dbLiteral + 1) / 2;
}
private double CityAvg(double dbMath, double dbLiteral)
{
return (dbMath + dbLiteral) / 2;
}
}
因此,我們的測試碼撰寫就可以變成如下:
public class DemoCalculateTests_Ver3
{
[Test]
public void NormalCaseTest()
{
// Arrange
string txtID = "Day-29";
string txtName = "SunShineYen";
string txtMath = "8";
string txtLiteral = "4";
string isCountry = "true";
DemoCalculate_Ver3 testObject
= new DemoCalculate_Ver3(txtID, txtName, txtMath, txtLiteral, isCountry);
// Act
string result = testObject.Calculate();
// Assert
Assert.AreEqual(result, "Day-29 SunShineYen 6.5");
}
[Test]
public void FailCaseTest_MathIsNotNumeric()
{
// Arrange
string txtID = "Day-29";
string txtName = "SunShineYen";
string txtMath = "Haha";
string txtLiteral = "4";
string isCountry = "not Important";
DemoCalculate_Ver3 testObject
= new DemoCalculate_Ver3(txtID, txtName, txtMath, txtLiteral, isCountry);
// Act
string result = testObject.CheckInitPara();
// Assert
Assert.AreEqual(result, "發生錯誤!數學數值應該是個 numeric!");
}
[Test]
public void FailCaseTest_MathIsInZeroToTen()
{
// Arrange
string txtID = "Day-29";
string txtName = "SunShineYen";
string txtMath = "12";
string txtLiteral = "4";
string isCountry = "not Important";
DemoCalculate_Ver3 testObject
= new DemoCalculate_Ver3(txtID, txtName, txtMath, txtLiteral, isCountry);
// Act
string result = testObject.CheckInitPara();
// Assert
Assert.AreEqual(result, "發生錯誤!數學數值應該介於 0 ~ 10 之間!");
}
[Test]
public void AvgCaseTest_Country()
{
// Arrange
string txtID = "Day-29";
string txtName = "SunShineYen";
string txtMath = "8";
string txtLiteral = "4";
string isCountry = "true";
DemoCalculate_Ver3 testObject
= new DemoCalculate_Ver3(txtID, txtName, txtMath, txtLiteral, isCountry);
// Act
string result = testObject.StudentAvg();
// Assert
Assert.AreEqual(result, "Day-29 SunShineYen 6.5");
}
[Test]
public void AvgCaseTest_City()
{
// Arrange
string txtID = "Day-29";
string txtName = "SunShineYen";
string txtMath = "8";
string txtLiteral = "4";
string isCountry = "false";
DemoCalculate_Ver3 testObject
= new DemoCalculate_Ver3(txtID, txtName, txtMath, txtLiteral, isCountry);
// Act
string result = testObject.StudentAvg();
// Assert
Assert.AreEqual(result, "Day-29 SunShineYen 6");
}
}
重構的手法百百種,在此示範的也不是說可適用於每種情況,只要在重構前與後的程式碼都能符合測試碼就算是合格的重構。不過經由重構的程式碼,通常其擴充性較佳,較能做後續的維護。