嗨,我有一个可能的设计缺陷,我需要用扩展方法解决它。 假设我有一个类,它有一个StringCollection属性。示例代码
public class MyProblematicClass
{
public IDbAccess Db{get;set;}
public StringCollection Errors{get;set;}
public MyProblematicClass(IDbAcces db){ Db=db;}
public int SetItem(Item i)
{
var id = Db.Save(i);
this.Errors = Db.Erros;
return id;
}
}
我正在做的是,在我的单元测试类中,我模拟了IDbAccess。此类根据属性验证对象。如果发生任何错误,它不会命中db,它只会填充自己的Errors集合。对于单元测试,我使用另一个只运行验证例程的dbclass,这里有问题我无法获得错误。让我给你一个进一步理解的例子(我知道设计是有问题的,但是现在我想处理它而不改变任何东西)
public static class MyDbExtension
{
public static Save(Item i)
{
Validation v = new Validation();
var erros = v.ValidateObject(i);
//Here is problem i cannot pass it to MyProblematicClass
if ( errors.Count > 0 )
return -1;
else
return 1;
/* what I want to is :
var stackTrace = new StackTrace(); get stack trace
var object = stackTrace.GetFrame(1).GetMethod().GetObject() or sth like that. get object
object.GetProperties()[0].SetValue(object,errors,null); find property and set it.
*/
}
}
在我的单元测试中:
public class UnitTest
{
Mock<IDbAccess> _db ;
MyProblematicClass _mpc;
pubic Setup()
{
_db.Setup(x=>x.Save(It.IsAny<Item>).Returns(u =>MyDbExtension.Save(u));
_mpc = new MyProblematicClass(_db.Object);
}
public void SetItem_EmptyObject_Contains3Erros()
{
Item i = new Item();
_mpc.SetItem(i);
//At this point i cannot set _mpc.Errors
}
我想要实现的是在我的DbExtension类中,我可以访问调用者类并设置其Errors属性吗?我尝试过,但现在还不太可能。如果有人有任何体面的解决方案我会很感激,当然你可以评论设计问题。
修改
我很欣赏Alex的回答他只是说忽略Save方法只是模拟Erros属性,它会没问题。这有道理,但我想知道的是,是否有可能访问Stack Trace并操纵调用者方法对象的属性?
提前致谢。
答案 0 :(得分:1)
您需要设置_db.Errors的返回值,如下所示:
public class UnitTest
{
Mock<IDbAccess> _db ;
MyProblematicClass _mpc;
StringCollection errors;
pubic Setup()
{
_db.Setup(x=>x.Save(It.IsAny<Item>).Returns(u =>MyDbExtension.Save(u));
_db.Setup(x=>x.Errors).Returns(errors);
_mpc = new MyProblematicClass(_db.Object);
}
public void SetItem_EmptyObject_ContainsError()
{
errors.Add("Expected Error!");
Item i = new Item();
_mpc.SetItem(i);
Assert.AreEqual("Expected Error!", _mpc.Errors[0]);
}
}
我必须承认我并没有真正遵循你的设计,你为什么要使用静态方法进行保存?你可以很容易地拥有这条线:
_db.Setup(x=>x.Save(It.IsAny<Item>).Returns(-1);
然后独立测试IDbAccess.Save()。
在'extension'类中,save方法没有返回值,MyProblematicClass在分配错误之前不检查返回值。
答案 1 :(得分:1)
不确定完全理解这个问题,但是您无法从正常程序访问堆栈中的参数。运行时元数据仅与静态信息(方法,属性,常量等)有关。
我相信只有一个调试器(它被认为是一个特殊的野兽)可以在不改变程序/源的情况下做到这一点,这会带来严重的性能损失。作为旁注,这里有一个链接,解释如何构建自己的托管调试器(.NET 4):CLR Managed Debugger (mdbg) Sample 4.0
另一个解决方案是检测代码(自动或使用工具)添加一些跟踪调用,该调用可以捕获每个跟踪方法的参数列表。像PostSharp这样的工具可以做到这一点。这是另一个链接:Non-Invasive Tracing & Logging
答案 2 :(得分:0)
您可以使用非托管调试API来访问调用堆栈并调用堆栈上的对象先前函数。
问题是,堆栈可能不包含您期望的方法。在内联和尾部调用优化的情况下,调用堆栈不包含先前调用的方法,这意味着您无法可靠地执行所需的操作。
有关详细信息,请参阅this answer by Eric Lippert。
答案 3 :(得分:0)
这不使用调用堆栈,但可能会给你一些里程:
class CalledClass
{
public static void PokeCaller()
{
Program._this.Error = "Error!!!";
}
}
class Program
{
public string Error = null;
[ThreadStatic] public static Program _this;
public void Run()
{
_this = this;
CalledClass.PokeCaller();
Console.WriteLine(Error);
Console.ReadKey();
}
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
}
制作错误[ThreadStatic]可能是一种更直接的方式...或者该主题的其他变体。您也可以将它与堆栈跟踪检查结合起来,看看在设置它之前是否实际上被具有“错误”属性的东西调用了......