I'm working on some test automation for a service, and figured out a neat way to roll up some common setup & verification into a 'session' class.
Conceptually, a test case might look like this:
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
}
In the Session object constructor I set up a connection to the service I'm testing with proper authentication per role etc, and in the session Dispose() method I have a common validation block that, for instance, checks that no server-side errors or warnings have been raised during the session lifetime.
Now, of course, this is sort of abusing the IDispose pattern, and if test code inside a using blocks throws an exception AND the validation block also throws an exception the second exception will mask the first.
Conceptually, if we have this scenario:
using (var managerSession = new Session(managerRole))
{
Assert.IsTrue(managerSession.DoJob(), "Manager did not do his job");
}
...and the assert fails or the call to managerSession.DoJob() throws an exception, then I would like the Session Dispose() method to skip the validation block, i.e.
public void Dispose()
{
if (NoExceptionThrown())
{
Assert.IsFalse(this.serviceConnection.HasErrors(), "Service connection has errors");
}
this.serviceConnection.Dispose();
}
...such that the test method never fails with 'Service connection has errors' if it actually fails with 'Manager did not do his job'
My question is: Is it at all possible to implement the 'NoExceptionThrown()' method here? Is there some global property that can be checked, or something hidden in Thread.CurrentThread that could be utilized?
Update:
My question is not how to refactor this :-)
I could of course use this pattern instead:
Session.ForRole(managerRole, (session) => { /* Test code here */ });
With the static method ForRole() defined like
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();
}
}
But I am curious whether there exists some way of grabbing the exception state as described above.
It would have been helpful if there were an overload of IDisposable.Dispose
which took a parameter of type Exception
to indicate what exception, if any, was pending in a finally
context associated with its cleanup. While a Dispose
method generally shouldn't care about the particulars of the exception, conditions may arise during the Dispose
method which should be reported to the caller. Any exception thrown from Dispose
will replace any exception that had been pending in the caller's finally
context, so it would be helpful if a Dispose
method could encapsulate a pending exception before replacing it. Unfortunately, no such feature exists and I do not expect any to be added.
While there are some hacks that may be used to achieve something like the desired effect, the only semantically-correct method is for the exception to be a parameter to a Dispose
method. The problem with any other approach is that a Dispose
may be run from within multiple nested finally
blocks, some of which have pending exceptions and some of which don't; code which examines the execution context to determine the status of the most deeply nested finally
block may fail if that block isn't the one which guards the lifetime of the object being disposed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With