Telerik blogs

Writing state of art unit tests that can validate your every part of the framework is challenging and interesting at the same time, its like becoming a samurai. One of the key concept in this is to keep our test synced all the time as underlying code changes and thus breaking them to the furthest unit as possible.  This also means, we should avoid  multiple conditions embedded in a single test.

Let’s consider the following example of transfer funds.

  1. var currencyService = Mock.Create<ICurrencyService>();
  2. //// current rate
  3. Mock.Arrange(() => currencyService.GetConversionRate("AUS", "CAD")).Returns(0.88);
  4.  
  5. Account to = new Account { Currency = "AUS", Balance = 120 };
  6. Account from = new Account { Currency = "CAD" };
  7.  
  8. AccountService accService = new AccountService(currencyService);
  9.  
  10. Assert.Throws<InvalidOperationException>(() => accService.TranferFunds(to, from, 200));
  11.  
  12. accService.TranferFunds(to, from, 100);
  13.  
  14. Assert.Equal(from.Balance, 88);
  15. Assert.Equal(20, to.Balance);

At first look,  it seems ok but as you look more closely , it is actually doing two tasks in one test. At line# 10 it is trying to validate the exception for invalid fund transfer and finally it is asserting if the currency conversion is successfully made.

Here, the name of the test itself is pretty vague. The first rule for writing unit test should always reflect to inner working of the target code, where just by looking at their names it is self explanatory. Having a obscure name for a test method not only increase the chances of cluttering the test code, but it also gives the opportunity to add multiple paths into it and eventually makes things messy as possible.

I would rater have two test methods that explicitly describes its intent and are more self-validating.

ShouldThrowExceptionForTransferringFromAccontWithNoFund
ShouldAssertTransferForExpectedConversionRate

Having, this type of breakdown also helps us pin-point reported bugs easily rather wasting any time on debugging for something more general and can minimize confusion among team members. Finally, we should always make our test F.I.R.S.T ( Fast.Independent.Repeatable.Self-Validating.Timely) [ Bob martin – Clean Code]. Only this will be enough to ensure, our test is as simple and clean as possible.

 

Hope that helps

--

Revisions:

Renamed ShouldThrowforInvalidFundsTransfer to a more specific as  “Invalid”, “Correct/Incorrect” words are vague as well [Community].

Return type for currency should be double instead of float in case for money [Community].


Comments

Comments are disabled in preview mode.