北京北大青鸟校区:Asp.net控件的单元测试

通常一个典型的asp.net控件至少会用ViewState存储一些属性,以便于在页面postback后不用重新设置。在这篇文章里,北京北大青鸟校区将介绍如何为控件写单元测试,以确保一个属性被正确的保存在ViewState里。
为了演示,北京北大青鸟校区老师先写了一个简单的控件。

    namespace Eilon.Sample.Controls {using System;using System.Web.UI;public class NewLabel : Control {public string Text {get {string s = ViewState[Text] as string;return s ?? String.Empty;}set {ViewState[Text] = value;}}protected override void Render(HtmlTextWriter writer) {writer.Write(Text);}}}

这个控件只是简单的将它唯一的属性Text输出。

下面,北京北大青鸟校区老师写一个简单的单元测试,以确保这个控件正确的工作。

    namespace Eilon.Sample.Controls.Test {using System;using System.IO;using System.Web.UI;using Microsoft.VisualStudio.TestTools.UnitTesting;[TestClass]public class NewLabelTest {[TestMethod]public void TextReturnsEmptyStringDefault() {NewLabel label = new NewLabel();Assert.AreEqual(String.Empty, label.Text,Default text should be empty string (not null));}[TestMethod]public void GetSetText() {const string value = Some Text;NewLabel label = new NewLabel();label.Text = value;Assert.AreEqual(value, label.Text,Property value isn't the same as what we set);}[TestMethod]public void RenderEmpty() {NewLabel label = new NewLabel();Assert.AreEqual(String.Empty, GetRenderedText(label),Shouldn't have rendered anything);}[TestMethod]public void RenderWithText() {const string value = Some Text;NewLabel label = new NewLabel();label.Text = value;Assert.AreEqual(value, GetRenderedText(label),Should have rendered the text);}private static string GetRenderedText(Control c) {HtmlTextWriter writer = new HtmlTextWriter(new StringWriter());c.RenderControl(writer);return writer.InnerWriter.ToString();}}}

看上去已经覆盖了100%的代码,是这样吗?事实上我们根本不能保证这个控件的属性已经被正确的存储到ViewState里。可是我们知道与ViewState有关的函数都是protected的,并不能从外部访问。解决这个问题,可以有很多办法,这里我们写一个internal interface,

    // Interface to expose protected methods from// the Control class to our unit testinternal interface IControl {void LoadViewState(object savedState);object SaveViewState();void TrackViewState();}
然后让我们的控件去实现它:

    #region IControl Membersvoid IControl.LoadViewState(object savedState) {LoadViewState(savedState);}object IControl.SaveViewState() {return SaveViewState();}void IControl.TrackViewState() {TrackViewState();}#endregion

现在就可以测试ViewState了:

    [TestMethod]public void TextSavedInViewState() {// Create the control, start tracking viewstate,// then set a new Text valueconst string firstValue = Some Text;const string secondValue = ViewState Text;NewLabel label = new NewLabel();label.Text = firstValue;((IControl)label).TrackViewState();label.Text = secondValue;// Save the control's stateobject viewState = ((IControl)label).SaveViewState();// Create a new control instance and load the state// back into it, overriding any existing valuesNewLabel newLabel = new NewLabel();label.Text = firstValue;((IControl)newLabel).LoadViewState(viewState);Assert.AreEqual(secondValue, newLabel.Text,Value restored from viewstate does not match the original value we set);}

这里注意一点,我们的接口是internal的,为了让测试用例可以访问它,需要添加

    using System.Runtime.CompilerServices;[assembly: InternalsVisibleTo(MyControlLibrary.Test)]
文章来源:北京北大青鸟校区学术部

北大青鸟网上报名
北大青鸟招生简章