無論正在開發或已經運行中的 ASP.NET MVC 網站服務,都不單單只有一條 Default Route 設定,在系統逐步長大下,Route 設定也逐漸複雜。
為確保日後「新增」or 「異動」時,不會被自己或他人改壞,加上單元測試來保護,應該是個不錯的作法。
● 2015-12-29 文章更新:加註內文使用「MvcRouteUnitTester」套件,進行測試開發。
● 2016-01-03 文章更新:調整 Source Code 專案結構,更新 Github Repo。
● 2016-01-14 文章更新:修改前言注意事項。
前言
以下就來介紹:使用外部 Library 來進行 Route 的單元測試開發。
因為程式看起來相當語意化,蠻容易上手的,有時間的話,可以嘗試撰寫看看。
紀錄
首先來看的是範例專案狀態。(這裡用的專案為:預設的 MVC 專案,以 HomeController 為例)
RouteConfig
namespace Sample.MVC
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults:
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}
);
}
}
}
Controller
namespace Sample.MVC.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
}
安裝套件:MvcRouteUnitTester
Install-Package MvcRouteUnitTester
MvcRouteUnitTester 方法簽章
MvcRouteUnitTester 驗證簽章
- ShouldMatchRoute:驗證「必須要有符合存在的 Route 設定」
- ShouldMatchNoRoute:驗證「必須不存在的 Route 設定」
- ShouldBeIgnored:驗證「必須是 Ignored 狀態的 Route 設定」
單元測試範例
依據上面 RouteConfig 設定,「預期 / 實際」都存在「Home/ Index」,所以就來一份單元測試來驗證。
測試案例:HomeIndex_RouteTest_Should_MatchRoue
當傳入「/」= 導到首頁;預設 Route 設定,就會跑到「Home/Index」,故配對後符合結果;得到一個「綠燈」。如下圖:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MvcRouteUnitTester;
using Sample.MVC;
using System.Web.Routing;
namespace Use_MvcRouteUnitTester_To_TestRoute.Routes
{
[TestClass]
public class RouteConfigTest
{
[TestMethod]
public void HomeIndex_RouteTest_Should_MatchRoute()
{
//// Arrange
var routes = new RouteCollection();
RouteConfig.RegisterRoutes(routes);
var expectedControllerName = "Home";
var expectedActionName = "Index";
var routeTester = new RouteTester(routes);
//// Act
var requsetInfo = routeTester.WithIncomingRequest("/");
//// Assert
requsetInfo.ShouldMatchRoute(expectedControllerName, expectedActionName);
}
}
}
接著就將所有與「Home/Index」相關的測試項目補上,我們期望收到「綠燈」測試通過的結果。
[TestMethod]
public void DefaultRoute_RouteTest()
{
//// Arrange
var routes = new RouteCollection();
RouteConfig.RegisterRoutes(routes);
var routeTester = new RouteTester(routes);
//// Assert
routeTester.WithIncomingRequest("/").ShouldMatchRoute("Home", "Index");
routeTester.WithIncomingRequest("/Home").ShouldMatchRoute("Home", "Index");
routeTester.WithIncomingRequest("/Home/Index").ShouldMatchRoute("Home", "Index");
}
當然為確保我們測試 Code 有正常的依據 RouteConfig 來運行,我們可以將 RouteConfig 預設 Route 做點小手腳,期望能拿到「紅燈」的結果。
將預設 Route,由「Home/Index」改成「Home/Index2」,在不動測試案例的情況下,會得到以下的結果:
我們預期是「Index」,但實際是「Index2」,所以可以看出來,它真的有依照我們 RouteConfig 的規則載運作;我們也就能慢慢將正式的規則加上。
下面來一段,BDD 測試範例程式
Feature: DefaultRouteFeature
為了確保網站的預設路由設定正確
讓運行可以正常運作
Scenario: 檢查出應該存在且正確的預設路由設定
Given 外部呼叫的網址為 "Home/Index"
When 在進入網站,經過路由配對流程之後
Then 配對結果,預期符合Controller為 "Home",Action為 "Index"
And 且這個比對結果是存在又正確的路由
Scenario: 檢查出不存在的預設路由設定
Given 外部呼叫的網址為 "Home/Index/1/Test"
When 在進入網站,經過路由配對流程之後
Then 配對結果,預期是一個不存在的路由
using MvcRouteUnitTester;
using Sample.MVC;
using System.Web.Routing;
using TechTalk.SpecFlow;
namespace Use_MvcRouteUnitTester_To_TestRoute.Routes
{
[Binding]
[Scope(Feature = "DefaultRouteFeature")]
public class DefaultRouteFeatureSteps
{
/// <summary>
/// ActionName
/// </summary>
public string ActionName { get; set; }
/// <summary>
/// ApiMethod
/// </summary>
public string ApiMethod { get; set; }
/// <summary>
/// ControllerName
/// </summary>
public string ControllerName { get; set; }
/// <summary>
/// RouteTester
/// </summary>
public RouteTester RouteTester { get; set; }
/// <summary>
/// RequestInfo
/// </summary>
public RequestInfo RequestInfo { get; set; }
/// <summary>
/// 運行測試前
/// </summary>
[BeforeScenario]
public void BeforeScenario()
{
var routes = new RouteCollection();
RouteConfig.RegisterRoutes(routes);
this.RouteTester = new RouteTester(routes);
}
/// <summary>
/// 設定ApiMethod
/// </summary>
/// <param name="method">method</param>
[Given(@"外部呼叫的網址為 ""(.*)""")]
public void Given外部呼叫的網址為(string method)
{
this.ApiMethod = method;
}
[When(@"在進入網站,經過路由配對流程之後")]
public void When在進入網站經過路由配對流程之後()
{
this.RequestInfo = this.RouteTester.WithIncomingRequest(this.ApiMethod);
}
[Then(@"配對結果,預期符合Controller為 ""(.*)"",Action為 ""(.*)""")]
public void Then配對結果預期符合Controller為Action為(string controllerName, string actionName)
{
this.ControllerName = controllerName;
this.ActionName = actionName;
}
[Then(@"且這個比對結果是存在又正確的路由")]
public void Then且這個比對結果是存在又正確的路由()
{
this.RequestInfo.ShouldMatchRoute(this.ControllerName, this.ActionName);
}
[Then(@"配對結果,預期是一個不存在的路由")]
public void Then配對結果預期是一個不存在的路由()
{
this.RequestInfo.ShouldMatchNoRoute();
}
}
}
綠燈
紅燈
最後補上一份比較複雜的路由設定 + 測試案例
1. 因為撰寫測試程式時,要注意有沒有打錯字。
2. 套件相依於: Moq (≥ 4.0.10827)
當然可以用的 Framework 不只有這一套,接下來,會再有幾篇來紀錄 Route 測試的做法。
結論
因為開發與維護運行許久的應用程式,可能有不少 RouteConfig 設定。
建議大家維護與開發 MVC 程式時,多加上 Route 相關測試,以便提升維護的信心與準確性。
參考資料
MvcRouteUnitTester Source Code:https://mvcrouteunittester.codeplex.com