Imagine you need to code the following requirements:
- When an exception is thrown the transaction must be rolled back
- The thrown exception must be rethrown
The productive code could look like:
public class SomeComponent
{
public void Do() {
ITransaction tx = this.session.BeginTransaction();
try {
this.session.Save(foo);
} catch(DataException e) {
tx.Rollback();
throw;
}
}
}
Now how would you write the requirements above in two separate unit tests? Here comes FluentAssertions into the play.
We just write a little extension method which ignores a specific kind of exception type and hook it up just after the fluent assertions extension method “Invoking”.
public static class CustomAssertionExtensions
{
public static void IgnoreAnyExceptions<TException>(this Action action)
where TException : Exception
{
try
{
action();
}
catch(TException)
{
}
}
}
And here the test code which uses it.
[TestFixture]
public class SomeComponentTest
{
private ITransaction transaction;
private ISession session;
private SomeComponent testee;
[SetUp]
public void SetUp()
{
this.transaction = A.Fake<ITransaction>();
this.session = A.Fake<ISession>();
this.SetupSessionReturnsTransaction();
this.testee = new SomeComponent(this.session);
}
[Test]
public void WhenExceptionOccurs_ShouldRollbackTransaction()
{
A.CallTo(() => this.session.Save(A<object>.Ignored)).Throws(new DataException());
this.testee.Invoking(t => t.Execute()).IgnoreAnyExceptions<DataException>();
A.CallTo(() => this.transaction.Rollback()).MustHaveHappened();
}
[Test]
public void WhenExceptionOccurs_ShouldRethrow()
{
A.CallTo(() => this.session.Save(A<object>.Ignored)).Throws(new DataException());
this.testee.Invoking(t => t.Execute()).ShouldThrow<DataException>();
}
private void SetupSessionReturnsTransaction()
{
A.CallTo(() => this.session.BeginTransaction()).Returns(this.transaction);
}
}
Hope this helps. I suggest you check out fluent assertions! Have fun. By the way the example was written using FakeItEasy. Be sure to check it out too!