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

  • Scala Programming Projects
  • Mikael Valot Nicolas Jorand
  • 627字
  • 2021-07-23 16:25:18

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.

主站蜘蛛池模板: 祁阳县| 土默特左旗| 阿克陶县| 金山区| 安徽省| 昆山市| 吉水县| 嘉义县| 泸水县| 怀远县| 镇安县| 囊谦县| 旬邑县| 霸州市| 九龙坡区| 噶尔县| 龙南县| 海原县| 重庆市| 乌拉特中旗| 阿克陶县| 沭阳县| 诸暨市| 林州市| 中超| 平和县| 武隆县| 安康市| 黎平县| 仙游县| 册亨县| 南丹县| 横山县| 昆明市| 垣曲县| 马山县| 宣恩县| 雷波县| 商丘市| 安吉县| 洛川县|