我们的团队刚刚开始进行单元测试和模拟,我们在考虑扩展方法时遇到了一些讨论。问题是什么是测试类的好方法,它使用扩展方法。即我们有这样的Enum ..
public enum State
{
[LangID(2817)]
Draft = 0,
[LangID(2832)]
Booked = 1,
[LangID(1957)]
Overdue = 2,
[LangID(2834)]
Checked = 3,
}
使用扩展方法:
public static string GetDescription(this Enum _enum)
{
Type type = _enum.GetType();
MemberInfo[] memInfo = type.GetMember(_enum.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(LangID), false);
if (attrs != null && attrs.Length > 0)
return LanguageDB.GetString(((LangID)attrs[0]).ID);
}
return _enum.ToString();
}
这将再次由被测试的类调用,如此......
public class SUT(){
public void MethodUnderTest(){
string description = SomeObject.Status.GetDescription();//Status is Type State
}
}
在这个例子中,enum通过LanguageDB获取用户语言的描述,遗憾的是,它不是在类中注入的,因为它是静态的。我们可以自然地折射这一批,但考虑到代码几乎完美无缺,这将是一笔巨大的投资。 有什么好建议吗?
答案 0 :(得分:7)
如果您正在使用MS'测试套件,您可以进行非常简单的重构,并且能够使用Accessors将模拟注入您的静态类型对象。
假设你有这样的代码:
public static void Save(this Entity data)
{
Repository.Instance.Save(data);
}
静态中的静态...难以测试?并不是的。这样修改扩展类:
private static Repository Instance
{
get
{
return _repository ?? Repository.Instance;
}
}
private static Repository _repository = null;
public static void Save(this Entity data)
{
Instance.Save(data);
}
简单的重构。现在,您可以使用访问器在测试时设置模拟...
[TestInitialize(), DebuggerStepThrough]
public void Setup()
{
MyEntityExtensions_Accessor._repository = new Mock<IRepository>();
}
答案 1 :(得分:5)
几乎完美无瑕? ; - )
规则1:如果无法测试,请不要写。
规则2:很多事情看起来像是生活会受到伤害,但却没有。
您对这次重构是一项重大投资有多确定?
规则0:规则是为了智者的指导和白痴的服从。
这是判断,你有多大可能通过工作避免该方法的缺陷?我的猜测是,在这种情况下,重构该方法的好处非常小。
答案 2 :(得分:1)
测试扩展方法本身的一个想法是使用一个私有方法来完成所做的所有工作获取注入的LanguageDB。 public方法将使用静态LanguageDB调用private方法,但实际上您将通过反射测试私有方法并传入模拟对象。
至于测试正在测试的实际类,你可能需要想出一种方法来替换静态LanguageDB - 也许只是将它取出并测试你得到的是实际的枚举名。毕竟,你已经测试了扩展在其他地方工作,所以你真的不需要在这个类中再次测试它。
最终,您发现静态对象很难测试。正如您已经猜到的那样,真正的答案是重构一个不依赖于静态对象的更好的设计(如果可以避免的话)(虽然这似乎是一个很好的候选者)。也许,你可以通过重构来包括注射。