2013-07-25 49 views
5

我正在为一项服务开展一些测试自动化工作,并找出一种简洁的方式将一些常见的设置&验证卷入“会话”类。如何检查是否有任何异常已被抛出?

从概念上讲,一个测试用例可能是这样的:

using (var managerSession = new Session(managerRole)) 
{ 
    // A manager puts some items in warehouse 
} 

using (var employeeSession = new Session(employeeRole)) 
{ 
    // An employee moves items from warehouse to store 
} 

using (var customerSession = new Session(customerRole)) 
{ 
    // A customer can buy items from the store 
} 

在Session对象构造我设置为我和每个角色等适当的认证测试服务的连接,并在会议上的Dispose ()方法我有一个通用的验证块,例如,检查在会话生存期内没有发生服务器端错误或警告。

当然,这是滥用IDispose模式,如果使用块内的测试代码引发异常,并且验证块也会引发异常,则第二个异常将掩盖第一个异常。

从概念上讲,如果我们有这样的场景:

using (var managerSession = new Session(managerRole)) 
{ 
    Assert.IsTrue(managerSession.DoJob(), "Manager did not do his job"); 
} 

...和断言失败或调用managerSession.DoJob()抛出一个异常,那么我想会话的Dispose()方法跳过验证模块,即

public void Dispose() 
{ 
    if (NoExceptionThrown()) 
    { 
     Assert.IsFalse(this.serviceConnection.HasErrors(), "Service connection has errors"); 
    } 

    this.serviceConnection.Dispose(); 
} 

...这样的测试方法从来没有失败,“服务连接有错误”,如果它实际上失败,“经理没做工作”,

我的问题是:是否可以在这里实现'NoExceptionThrown()'方法?是否有一些全局属性可以检查,或者可以使用Thread.CurrentThread中隐藏的某些东西?

更新:

我的问题是如何重构这个:-)

我当然可以使用这个模式来代替:

Session.ForRole(managerRole, (session) => { /* Test code here */ }); 

用静态方法ForRole()如

public static void ForRole(Role r, Action<Session> code) 
{ 
    var session = new Session(r); 
    try 
    { 
     code(session); 
     Assert.IsFalse(session.serviceConnection.HasErrors()); 
    } 
    finally 
    { 
     session.Dispose(); 
    } 
} 

但是我很好奇是否存在某种如上所述的抓取异常状态的方式。

+2

两件事。首先,是的,这是滥用'IDisposable'。使用'IDisposable'来表示“我拥有应该尽快释放的非托管资源”。使其意味着别的东西使你的代码难以阅读。其次,你是否担心*任何*异常,或者只*未被捕获*异常?假设测试代码抛出一个异常,捕获它并正常完成。应该标记? –

+0

啊,我只关心_uncaught_异常。我会更新这个问题...... – Christoffer

+0

至于滥用IDisposable:我实际上更关心的是错过每次会话的常见验证,而不是潜在的错误掩盖,所以这个问题更多的是“很高兴有”功能。实际的测试代码块包含10-100行UI自动化代码,并且我们已经发现缺陷未被发现,因为验证步骤中的一个在其中一个块中错过了(或报告在错误的块中)。 – Christoffer

回答

1

这本来是有益的,如果有历时Exception类型的参数来表示什么异常,如果有的话,是在用其清理相关的finally方面悬而未决的IDisposable.Dispose过载。虽然Dispose方法通常不应该关心例外的细节,但在应该向呼叫方报告的方法期间可能出现条件。从Dispose中抛出的任何异常都会替换调用者的finally上下文中未处理的任何异常,所以如果Dispose方法可以在替换之前封装未决的异常,将会有所帮助。不幸的是,没有这样的功能存在,我不希望有任何添加。

虽然有些黑客可能用于实现类似所需的效果,但唯一符合语义的方法是将例外作为Dispose方法的参数。任何其他方法的问题是Dispose可能在多个嵌套finally块内运行,其中一些块有未决的异常,其中一些没有;检查执行上下文以确定嵌套最深的finally块的状态的代码可能会失败,如果该块不是保护被丢弃对象的生命周期的块。

+0

我想我会接受这个答案作为最正确的答案 – Christoffer

0

好吧,一个确定的,但丑陋的方法是在你的课堂上有一个布尔(默认为false)。

using (var managerSession = new Session(managerRole)) 
{ 
    Assert.IsTrue(managerSession.DoJob(), "Manager did not do his job"); 
    managerSession.NoExceptionThrown = true; 
} 

我不认为你会比你的IDisposable模式更清洁。

在这种情况下,try ... catch ...可能会更好,因为您可以直接访问异常对象,您可以测试它是否为null(这是一个单词吗?甚至存储它以备后用。你不应该失去任何东西,因为using最终只是一个尝试抓住糖。

+1

是的,无效是一个完美的词。 – Christoffer

+0

而对于更严重的说法,在Dispose方法中设置控制验证块的属性实际上并不是一个好主意。如果我们在每个使用块上添加一些东西,那么最好将现有的验证代码重构为一个单独的方法,并在关闭范围之前调用该方法 – Christoffer

+0

@Christoffer是的,我想你正在尝试做一个使用圆形插入方孔的东西。正如supercat指出的那样,正确的方法是传递异常对象或者是否有处理异常的指示。所以,我想你可以添加适当的方法,只是抛弃使用并自己调用Dispose(exception)?那里也存在一些问题,我认为你不能强迫这种语言按照你喜欢的方式去做,而不去掉它,并且向后重新贴上它的头:c。据我所知,没有办法检查catch块之外的任何异常。 – Xcelled194