1.前言
現在這個項目已經有階段性的模塊完成了,所以就想着對這些模塊進行單元測試,以保證項目的代碼的質量。首先雖然標題是mvc+webapi實質上我只是對mvc進行的測試。用的時候vs的unit test generator(2015自帶).至於它版本的安裝和介紹在這不做詳細介紹。還有其實我也任務單元測試就是個雞肋。第一它浪費時間,第二他也不能完全保證我的代碼完全沒有問題,第三我的測試並不能算到的績效當中去。所以我把它當做對我一個階段代碼的梳理來對待。
2.單元測試建立
2.1新建單元測試項目
此項目我是把它當作模塊插件來處理。
2.2.添加單元測試類

[TestClass] public class mtnfim { //實例化測試mvc Controller mgtController mtn_fim = new mgtController(); [TestMethod] public void IndexTest() { //調用類中方法 var ret = mtn_fim.Index() as ViewResult; int errcode = testresult.action_result(ret).errcode; Assert.AreEqual(0, errcode); } }
2.3.代碼解釋如下:
2.3.1Assert類的使用
Assert.AreEqual() 測試指定的值是否相等,如果相等,則測試通過;
AreSame() 用於驗證指定的兩個對象變量是指向相同的對象,否則認為是錯誤
AreNotSame() 用於驗證指定的兩個對象變量是指向不同的對象,否則認為是錯誤
Assert.IsTrue() 測試指定的條件是否為True,如果為True,則測試通過;
Assert.IsFalse() 測試指定的條件是否為False,如果為False,則測試通過;
Assert.IsNull() 測試指定的對象是否為空引用,如果為空,則測試通過;
Assert.IsNotNull() 測試指定的對象是否為非空,如果不為空,則測試通過;
2.3.2.返回類型的處理
我項目當中mvc Controller的返回類型有兩個JsonResult。ActionrResult.兩種,所以針對於assert類的使用顯然我要對mvc的返回的對象進行處理。
jsonresult的處理方式:
通過nugt下載ExposedObject把 對象轉為dynami然后對其對象進行操作。代碼如下。

var jsonret = mtn_fim.add_feeitem("測試", "cs"); var ret = Exposed.From(jsonret.Data); int errcode = ret.errcode; Assert.AreEqual(6002, errcode);
ActionrResult的處理:
相對與jsonresult直接操作返回對象即可,對此我寫了一個方法。

public static class testresult { public static result<object> action_result(ViewResult ret) { return ((wx.web.result<object>)(((System.Web.Mvc.ViewResultBase)((ret))).Model)); } public static result apicitroller_result(System.Web.Http.IHttpActionResult action) { var rs = ((System.Web.Http.Results.OkNegotiatedContentResult<wx.web.result>) (action)).Content; return rs; } }
使用如下:

var ret = mtn_fim.Index() as ViewResult; int errcode = testresult.action_result(ret).errcode; Assert.AreEqual(0, errcode);
3.實現之外的事:
3.1.數據庫初始化
以上已經初步實現了單元測試的基本實現,但是考慮到單元測試重復測試,我用ef code first動態的數據庫創建,和需要測試的數據。

public void Initialize(FeeDbContext context) { List<organization> orgs = new List<organization>() { new organization(){oid=1, name="新中新集團1", state=true,reg_time=DateTime.Now}, new organization(){oid=2, name="山東大學", state=true,reg_time=DateTime.Now}, new organization(){oid=3, name="浙江大學", state=true,reg_time=DateTime.Now}, new organization(){oid=4, name="同濟大學", state=true,reg_time=DateTime.Now} }; List<fee_item> fim = new List<fee_item>() { new fee_item(){name="電費",code="pow",type= feeItemType.Normal,State=true,Icon="img/jf_dianf.png",sort=0,appid=0,reg_time=DateTime.Now}, new fee_item(){name="網費",code="net",type= feeItemType.Normal,State=true,Icon="img/jf_wangf.png",sort=1,appid=1,reg_time=DateTime.Now}, new fee_item(){name="四六級",code="cet",type= feeItemType.Normal,State=true,Icon="img/jf_siliuj.png",sort=2,appid=2,reg_time=DateTime.Now}, new fee_item(){name="報名費",code="pow",type= feeItemType.Normal,State=true,Icon="img/jf_baomingf.png",sort=3,appid=3,reg_time=DateTime.Now}, new fee_item(){name="保險費",code="pow",type= feeItemType.Normal,State=false,Icon="img/jf_baox.png",sort=4,appid=4,reg_time=DateTime.Now}, new fee_item(){name="住宿費",code="pow",type= feeItemType.Normal,State=true,Icon="img/jf_dianf.png",sort=5,appid=5,reg_time=DateTime.Now} }; List<school_power> con_power = new List<school_power>() { new school_power(){oid=1,power_code="ykt",power_id="1",reg_time=DateTime.Now}, new school_power(){oid=1,power_code="sims",power_id="2",reg_time=DateTime.Now} }; //depts.ForEach(o => context.dept.Add(o)); orgs.ForEach(o => context.organization.Add(o)); fim.ForEach(o => context.fee_item.Add(o)); con_power.ForEach(o => context.control_power.Add(o)); context.SaveChanges(); }

public void initial() { try { var context = new FeeDbContext(); if (context.Database.Exists()) { context.Database.Delete(); } new List<IDataInitializer<FeeDbContext>>() { new DataInit4dept() }.Setup<FeeDbContext>(context); } catch (DbEntityValidationException ex) { StringBuilder error = new StringBuilder(); foreach (var item in ex.EntityValidationErrors) { foreach (var item2 in item.ValidationErrors) { error.Append(string.Format("{0}:{1}\r\n", item2.PropertyName, item2.ErrorMessage)); } } Console.WriteLine("數據庫初始化報錯:" + error); throw ex; } catch (Exception e) { Console.WriteLine("數據庫初始化報錯:" + e.Message); throw e; } }
ef code first寫好之后就又到單元測試的部分了,所以要想每次執行單元測試內容的時候都執行一下以上內用就要用到單元測試的附屬屬性:
[ClassInitialize()] 在運行類的第一個測試前先運行代碼
[ClassCleanup()] 在運行完類中的所有測試后再運行代碼
[TestInitialize()] 在運行每個測試前先運行代碼
[TestCleanup()] 在運行完每個測試后運行代碼

[ClassInitialize] public static void initialdb(TestContext context) { //初始化數據庫 new DataBuilder().initial(); //加載webapi bin文件 apiconfig_factory.Instance.Load_Apis(); }
運行測試結果如下:
直到這單元測試的實現也就算結束了,但是對於單元測試還有很多東西沒有往上寫,比如測試模型,覆蓋率等等,對於這個項目的測試也不僅限於單元測試,后邊的性能測試和ui測試都會抽時間進行總結。
3.使用Moq模擬AspnetMvc中的Request.Form
3.1nuget下載安裝moq
3.2.實例
1、比如Request.Form["MingCheng"]為control中需要模擬的內容
2、在單元測試中調用的代碼
var formvalues = new NameValueCollection { { "ST_MingCheng", "" } };//可以選用KeyValuePair var request = new NameValueCollection { { "pageIndex", "" } }; var mockHttpContext = new Mock<HttpContextBase>(); mockHttpContext.Setup(c => c.Request.QueryString).Returns(request); mockHttpContext.Setup(c => c.Request.Form).Returns(formvalues); Mock<SMSTemplateController> mockEnterprise = new Mock<SMSTemplateController>(); var context = new ControllerContext(mockHttpContext.Object, new RouteData(), stc); stc.ControllerContext = context; //設置control的context熟悉 var result = stc._SMSTemplatelist(); //調用action方法