通常单元测试需要访问受测类的私有方法或属性,一般需要使用VSTS自动生成的访问器,但是对于该类的父类私有属性就无法访问了,这也是上一篇博文提到的状况。其实我们还可以自己编写访问器,这样就不需要受到系统生成的访问器的束缚(由于生成的访问器没有源码无法修改)。下文就是一个范例(受测项目BankDemo.csproj,程序集BankDemo.exe):
========受测类的实现代码:========
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BankDemo
{
public class BankAccount
{
private int _accountNo;
private float _currentBalance;
private static float _bankAsset;
private static int _newUniqueAccountNo;
private static int GetUniqueAccountNo
{
get { return ++_newUniqueAccountNo; }
}
private float CurrentBalance
{
get { return _currentBalance; }
set { _currentBalance = value; }
}
// Constructors for new account
private BankAccount(float initialBalance)
{
this._accountNo = GetUniqueAccountNo;
this._currentBalance = initialBalance;
_bankAsset += initialBalance;
}
// Methods
private float DepositMoney(float depositAmount)
{
this.CurrentBalance += depositAmount;
_bankAsset += depositAmount;
return this.CurrentBalance;
}
private float WithdrawMoney(float withdrawAmount)
{
this.CurrentBalance -= withdrawAmount;
_bankAsset -= withdrawAmount;
return this.CurrentBalance;
}
private static float BankAsset()
{
return _bankAsset;
}
}
}
========访问器代码可以这样实现:========
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestProject1
{
class BankAccountAccessor
{
PrivateObject _privateObject;
static PrivateType _privateType = new PrivateType("BankDemo", "BankDemo.BankAccount");
public BankAccountAccessor(object target)
{
_privateObject = new PrivateObject(target, _privateType);
}
public static object CreatePrivate(float initialBalance)
{
object[] args = new object[] { initialBalance };
PrivateObject priv_obj = new PrivateObject("BankDemo", "BankDemo.BankAccount", new System.Type[] { typeof(float) }, args);
return priv_obj.Target;
}
public float DepositMoney(float depositAmount)
{
object[] args = new object[] { depositAmount };
return ((float)(_privateObject.Invoke("DepositMoney", new Type[] { typeof(float) }, args)));
}
public float WithdrawMoney(float withdrawAmount)
{
object[] args = new object[] { withdrawAmount };
return ((float)(_privateObject.Invoke("WithdrawMoney", new Type[] { typeof(float) }, args)));
}
public static float BankAsset()
{
object[] args = new object[0];
return ((float)(_privateType.InvokeStatic("BankAsset", new System.Type[0], args)));
}
public float CurrentBalance
{
get { return ((float)(_privateObject.GetProperty("CurrentBalance"))); }
set { _privateObject.SetProperty("CurrentBalance", value); }
}
public static int GetUniqueAccountNo
{
get { return ((int)(_privateType.GetStaticProperty("GetUniqueAccountNo"))); }
}
public int _accountNo
{
get { return ((int)(_privateObject.GetField("_accountNo"))); }
set { _privateObject.SetField("_accountNo", value); }
}
}
}
========单元测试就是如下(这里仅仅举其中一个私有方法测试为例):========
public void WithdrawMoneyTest()
{
float initBalance = 5000f;
object param0 = BankAccountAccessor.CreatePrivate(initBalance);
BankAccountAccessor target = new BankAccountAccessor(param0);
float withdrawAmount = 100F;
float expected = initBalance - withdrawAmount;
float actual;
actual = target.WithdrawMoney(withdrawAmount);
Assert.AreEqual(expected, actual);
}