但是 Visual Studio 還可以對類的私有方法進(jìn)行測試。而Visual Studio 不允許測試代碼跟實(shí)際代碼放在一個項(xiàng)目中,我們來看看是Visual Studio UnitTest如何做的。
比如我們有這樣一個私有方法
namespace ClassLibrary1{
public class DivisionClass{
private int Divide_private(int numerator, int denominator){
return numerator / denominator;
}
}
}
我們只要在這個私有方法的右鍵菜單中選擇 創(chuàng)建單元測試,系統(tǒng)就自動產(chǎn)生了這個私有方法的單元測試代碼。
下面我們來分析產(chǎn)生的單元測試代碼,看Visual Studio UnitTest 是如何對私有方法進(jìn)行單元測試的
簡單來說,Visual Studio UnitTest 生成私有方法的單元測試時,將自動創(chuàng)建一個私有訪問器。私有訪問器是測試方法用于訪問私有代碼的方法。單元測試生成對私有訪問器的調(diào)用,然后通過私有訪問器來調(diào)用私有方法。私有訪問器駐留在測試項(xiàng)目中的文件中;因此將被編譯為測試項(xiàng)目程序集。
具體來看測試項(xiàng)目:
首先我們可以看到一個名為 VSCodeGenAccessors.cs 的新文件被創(chuàng)建,
這個文件包含兩個類:
internal 類型的 BaseAccessor 類 和 派生自它的 ClassLibrary1_DivisionClassAccessor 類
BaseAccessor 類 是通用的訪問器基類。
ClassLibrary1_DivisionClassAccessor 類 則是對你要訪問類的私有方法進(jìn)行了反射封裝,這樣你就可以通過操作這個類來操作該私有方法了。如下面代碼:
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestProject1{
[System.Diagnostics.DebuggerStepThrough()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
internal class BaseAccessor {
protected Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject m_privateObject;
protected BaseAccessor(object target, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type) {
m_privateObject = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(target, type);
}
protected BaseAccessor(Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type) :
this(null, type) {}
internal virtual object Target {
get {
return m_privateObject.Target;
}
}
public override string ToString() {
return this.Target.ToString();
}
public override bool Equals(object obj) {
if (typeof(BaseAccessor).IsInstanceOfType(obj)) {
obj = ((BaseAccessor)(obj)).Target;
}
return this.Target.Equals(obj);
}
public override int GetHashCode() {
return this.Target.GetHashCode();
}
}
[System.Diagnostics.DebuggerStepThrough()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
internal class ClassLibrary1_DivisionClassAccessor : BaseAccessor {
protected static Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType m_privateType = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType(typeof(global::ClassLibrary1.DivisionClass));
internal ClassLibrary1_DivisionClassAccessor(global::ClassLibrary1.DivisionClass target) :
base(target, m_privateType) {}
internal int Divide_private(int numerator, int denominator) {
object[] args = new object[] {
numerator,
denominator};
int ret = ((int)(m_privateObject.Invoke("Divide_private", new System.Type[] {
typeof(int),
typeof(int)}, args)));
return ret;
}
}
}
注意:當(dāng)您更改正在測試的代碼文件中的私有方法時,這個訪問器可能無法正常工作,需要重新生成專用訪問器(ClassLibrary1_DivisionClassAccessor 類)。
整理一下就是:
這個訪問器,是通過反射的方式實(shí)現(xiàn)的。
VSUT利用自動代碼生成技術(shù),在單元測試項(xiàng)目中先來給你要測試的類生成一個名字叫XXXAccessor的訪問器。
這個訪問器會把需要測試類的需要測試的私有方法暴露出來,這種方式,無論是私有還是公共的屬性和方法。這種方式可以很方便的給測試方法搭建測試環(huán)境,MOCK對象的注入也容易了。在測試調(diào)用的時候,就簡單的只有下面的代碼了:
[DeploymentItem("ClassLibrary1.dll")]
[TestMethod()]
public void Divide_privateTest()
{
DivisionClass target = new DivisionClass();
TestProject1.ClassLibrary1_DivisionClassAccessor accessor = new TestProject1.ClassLibrary1_DivisionClassAccessor(target);
int numerator = 4;
int denominator = 0;
int expected = 0;
int actual;
actual = accessor.Divide_private(numerator, denominator);
Assert.AreEqual(expected, actual, "ClassLibrary1.DivisionClass.Divide_private 未返回所需的值。");
Assert.Inconclusive("驗(yàn)證此測試方法的正確性。");
}