官术网_书友最值得收藏!

Writing a unit test for the accumulation phase

We want a function that behaves similarly to the FV function in Excel: it calculates the future value of an investment based on a constant interest rate. As we follow a TDD approach, the first thing to do is create a failing test:

  1. Create a new Scala project called retirement_calculator. Follow the same instructions as in Chapter 1Writing Your First Program.
  1. Right-click on the directory src/main/scala and select New | Package. Name it retcalc.
  2. Right-click on the new package and select New | Scala class. Name it RetCalcSpec.
  3. Enter the following code:
package retcalc

import org.scalactic.{Equality, TolerantNumerics, TypeCheckedTripleEquals}
import org.scalatest.{Matchers, WordSpec}

class RetCalcSpec extends WordSpec with Matchers with TypeCheckedTripleEquals {

implicit val doubleEquality: Equality[Double] =
TolerantNumerics.tolerantDoubleEquality(0.0001)

"RetCalc.futureCapital" should {
"calculate the amount of savings I will have in n months" in {
val actual = RetCalc.futureCapital(
interestRate = 0.04 / 12, nbOfMonths = 25 * 12,
netIncome = 3000, currentExpenses = 2000,
initialCapital = 10000)
val expected = 541267.1990
actual should ===(expected)
}
}

As seen in Chapter 1Writing Your First Program, in the section Creating my first project, we used the WordSpec ScalaTest style. We also used a handy feature called TypeCheckedTripleEquals. It provides a powerful assertion, should ===, that ensures at compile time that both sides of the equality have the same type. The default ScalaTest assertion should verifies the type equality at runtime. We encourage you to always use should ===, as it will save a lot of time when refactoring code.

Besides, it lets us use a certain amount of tolerance when comparing double values. Consider the following declaration:

implicit val doubleEquality: Equality[Double] =
TolerantNumerics.tolerantDoubleEquality(0.0001)

It will let a double1 should === (double2) assertion pass if the absolute difference between double1 and double2 is lower than 0.0001. This allows us to specify expected values to only the fourth digit after the decimal point. It also avoids hitting floating point calculation issues. For instance, enter the following code in the Scala Console:

scala> val double1 = 0.01 -0.001 + 0.001
double1: Double = 0.010000000000000002

scala> double1 == 0.01
res2: Boolean = false

It can be a bit surprising, but this is a well-known problem in any language that encodes floating point numbers in binary. We could have used BigDecimal instead of Double to avoid this kind of issue, but for our purposes, we do not need the additional precision offered by BigDecimal, and BigDecimal computations are much slower.

The body of the test is quite straightforward; we call a function and expect a value. As we wrote the test first, we had to work out what would be the expected result before writing the production code. For non-trivial calculations, I generally use Excel or LibreOffice. For this function, the expected value can be obtained by using the formula =-FV(0.04/12,25*12,1000,10000,0). We assume that the user saves the full difference between his or her income and his/her expenses every month. Hence, the PMT parameter in the FV function is 1,000 = netIncome - currentExpenses.

We now have a failing test, but it does not compile, as the RetCalc object and its futureCapital function do not exist yet. Create a RetCalc object in a new package retcalc in src/main/scala, then select the red futureCapital call in the unit test and hit Alt + Enter to generate the function body. Fill in the names and types of the parameters. You should end up with the following code in RetCalc.scala:

package retcalc

object RetCalc {
def futureCapital(interestRate: Double, nbOfMonths: Int, netIncome:
Int, currentExpenses: Int, initialCapital: Double): Double = ???
}

Open RetCalcSpec, and type Ctrl + Shift + R to compile and run it. Everything should compile and the test should fail.

主站蜘蛛池模板: 民勤县| 津南区| 廉江市| 大安市| 桂东县| 望江县| 九龙坡区| 宁夏| 宣化县| 互助| 色达县| 安阳市| 陈巴尔虎旗| 横山县| 通州区| 千阳县| 济南市| 元阳县| 晋中市| 平乐县| 沙雅县| 吉水县| 吴旗县| 高邑县| 沈丘县| 安宁市| 吴桥县| 张家口市| 陕西省| 满洲里市| 新安县| 定西市| 桃园县| 屯门区| 孝义市| 塔河县| 宿松县| 昆山市| 克山县| 密山市| 平山县|