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!