if (!string.IsNullOrEmpty(JsonResult)) { var APIResult = JsonConvert.DeserializeObject<List<PTXBusRouteResult>>(JsonResult);
if (APIResult != null && APIResult.Count > 0) { var Route = APIResult.First(); Result = new BusRouteDTO { Name = Route.RouteName.Zh_tw, BusStops = new List<BusRouteDTO.BusStop>() };
foreach (var stop in Route.Stops) { Result.BusStops.Add(new BusRouteDTO.BusStop { ID = stop.StopUID, Name = stop.StopName.Zh_tw }); } } }
return Result; } }
修改Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classProgram { staticvoidMain(string[] args) { //new PTX時變成要帶入呼叫API的實體 var PTXFunction = new PTX(new MyRestSharp());
[TestClass()] publicclassPTXTests { [TestMethod()] publicvoid GetTest_傳入縣市和公車路線名稱_如果查的到資料_應回傳該路線的BusRouteDTO() { //arrange var RestSharpMock = new MyRestSharpMock(); var Sut = new PTX(RestSharpMock); //注入我們模擬的假物件MyRestSharpMock var City = "Taipei"; var RouteName = "307";
var expected = "307"; //act var actual = Sut.Get(City, RouteName);
如果今天上線後發現錯誤,而發生錯誤的地方是在呼叫API的程式,那該修改的是誰?當然是MyRestSharp.cs,也絕對不是PTX這支程式,因為它職責掌管的邏輯全部都是正確的。而我們現在單元測試以及MyRestSharpMock都是在做什麼? 都是在輔助我們驗證PTX所有包含到的邏輯,我們並不關注外部其他程式到底幹了什麼,退一萬步說(好啦其實退一步就可以了)(你好煩)今天IRestSharp這個Interface你的夥伴忘記去實作導致程式上線壞了,那也不會是改你的PTX吧,要改可以,over my dead body先,所以希望這囉說的補充可以幫助釐清這件事情。
[TestClass()] publicclassPTXTests { [TestMethod()] publicvoid GetTest_傳入縣市和公車路線名稱_如果查的到資料_應回傳該路線的BusRouteDTO() { //arrange //透過NSubstitute跟它說你想Mock實作的Interface var NsubRestSharpMock = Substitute.For<IRestSharp>(); var Sut = new PTX(NsubRestSharpMock); //注入
//期望API回傳的Json var sb = new System.Text.StringBuilder(12766); sb.AppendLine(@"[{""RouteUID"":""TPE16111"",""RouteID"":""16111"",""RouteName"":...後略");
//這邊的意思是,當這個透過NSubstitute Mock的物件 //被呼叫Get的方法時,且帶入的參數是我們這邊寫的字串 //就回傳Returns那裡面我們設定的字串 NsubRestSharpMock.Get("http://ptx.transportdata.tw/MOTC/v2/Bus/StopOfRoute/City/Taipei/307?%24top=1&%24format=JSON") .Returns(sb.ToString()); var City = "Taipei"; var RouteName = "307";
var expected = "307"; //act var actual = Sut.Get(City, RouteName);
//Use RestSharp Call API var client = new RestClient($"http://ptx.transportdata.tw/MOTC/v2/Bus/StopOfRoute/City/{city}/{routeName}?%24top=1&%24format=JSON"); var request = new RestRequest(Method.GET); request.AddHeader("cache-control", "no-cache"); IRestResponse response = client.Execute(request);
if (response.StatusCode == HttpStatusCode.OK) { var APIResult = JsonConvert.DeserializeObject<List<PTXBusRouteResult>>(response.Content);
if (APIResult != null && APIResult.Count > 0) { var Route = APIResult.First(); Result = new BusRouteDTO { Name = Route.RouteName.Zh_tw, BusStops = new List<BusRouteDTO.BusStop>() };
foreach (var stop in Route.Stops) { Result.BusStops.Add(new BusRouteDTO.BusStop { ID = stop.StopUID, Name = stop.StopName.Zh_tw }); } } }
[TestClass()] publicclassPTXTests { [TestMethod()] publicvoid GetTest_傳入縣市和公車路線名稱_如果查的到資料_應回傳該路線的BusRouteDTO() { //arrange var Sut = new PTX(); var City = "Taipei"; var RouteName = "307";
var expected = "307"; //act var actual = Sut.Get(City, RouteName);
if (!string.IsNullOrEmpty(JsonResult)) { var APIResult = JsonConvert.DeserializeObject<List<PTXBusRouteResult>>(JsonResult);
if (APIResult != null && APIResult.Count > 0) { var Route = APIResult.First(); Result = new BusRouteDTO { Name = Route.RouteName.Zh_tw, BusStops = new List<BusRouteDTO.BusStop>() };
foreach (var stop in Route.Stops) { Result.BusStops.Add(new BusRouteDTO.BusStop { ID = stop.StopUID, Name = stop.StopName.Zh_tw }); } } }
return Result; }
///<summary> /// Call API ///</summary> ///<param name="city">縣市</param> ///<param name="routeName">巴士路線名稱</param> ///<returns></returns> privatestringCallAPI(string city, string routeName) { //Use RestSharp Call API var client = new RestClient($"http://ptx.transportdata.tw/MOTC/v2/Bus/StopOfRoute/City/{city}/{routeName}?%24top=1&%24format=JSON"); var request = new RestRequest(Method.GET); request.AddHeader("cache-control", "no-cache"); IRestResponse response = client.Execute(request);
if (response.StatusCode == HttpStatusCode.OK) { return response.Content; }
[TestMethod()] publicvoid GetTest_傳入縣市和公車路線名稱_如果查的到資料_應回傳該路線的BusRouteDTO() { //arrange var Sut = new PTXStub(); //改成PTXStub var City = "Taipei"; var RouteName = "307";
//API應該回傳的結果 var sb = new System.Text.StringBuilder(12766); sb.AppendLine(@"[{""RouteUID"":""TPE16111"",""RouteID"":""16111"",""RouteName"":{""Zh_tw"":""307"",""En"":""307""},""KeyPattern"":false,""SubRouteUID"":""TPE157462"",""SubRouteID"":""157462"",""SubRouteName"":{""Zh_tw"":""307莒光經板橋前站"",""En"":""307""},""Direction"":0,""Stops"":[{""StopUID"":""TPE15294"",""StopID"":""15294"",""StopName"":{""Zh_tw"":""莊敬里"",]............");
namespaceUnitTestDay1.Tests { [TestClass()] publicclassEasyMethodTests { [TestMethod()] publicvoid Method1Test_呼叫時應回傳結果為7() { //arrange var Sut = new EasyMethod(); var expected = 7;
//act var actual = Sut.Method1();
//assert Assert.AreEqual(expected, actual); }
[TestMethod()] publicvoid Method2Test_呼叫時應回傳結果為10() { //arrange var Sut = new EasyMethod(); var expected = 10;
[Validator(typeof(ParameterValidator))] publicclassWantValidateParameter { publicstring ID { get; set; }
publicstring Name { get; set; } }
6.在Action裡面就可以用ModelState.IsValid來得知參數是否驗證成功啦!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
public ActionResult Test(WantValidateParameter parameter) { string Result = string.Empty; if (!ModelState.IsValid) { //取得錯誤的訊息 var Error = ModelState.Values.SelectMany(x => x.Errors).FirstOrDefault(); Result = Error.ErrorMessage; } else { Result = "驗證成功"; }