替换仅在 Scala 中的特定单元测试中使用的方法

Replace a method for usage only in a particular unit tests in Scala

是否可以使用版本 2.11 和 scalatest 3.0.4 替换仅用于特定单元测试的方法?

这是我的场景: 我有方法

import java.security.SecureRandom
import org.bouncycastle.util.encoders.Hex

class KeyHasher extends Serializable {

  // getSalt returns a random number that servers as salt for the actual hash algorithm  
  def getSalt(length): String = {
    val salt: Array[Byte] = new Array[Byte](length)
    secureRandom.nextBytes(salt)
    Hex.toHexString(hashedKey)
  }

  def getEncodedKey(actualKey: String): String = {
  ...
  val salt = getSalt() 
  ...
  return applyHash(salt) // apply a hash algorithm using a salt
  }

}

对于特定的单元测试,我想确保方法 getSalt returns 是一个常量,而不是随机数。

我知道,我可以在 getEncodedKey 方法中使用额外的输入参数 (testFlag) 或在 getSalt 方法中使用全局变量来获得解决方案。但是,我想知道是否有一种方法可以不触及 getEncodedKey 并且只在一个单元测试中实现一些东西。

一个简单的方法是创建一个像 SaltGenerator 这样的特征,然后实现你的方法使用的特征,这将允许你创建一个 SatlGenerator 其中 returns测试时保持不变。

例如:

trait SaltGenerator {
  def getSalt(length): String
}

class KeyHasher(saltGenerator: SaltGenerator) {...}

然后,在编写测试时,只需创建 returns 常量的生成器:

val saltGenerator = new SaltGenerator {
  def getSalt(length): String = ???
}

虽然您可以找到一种方法来模拟现有方法,但这总是很棘手并且难以维护。如果您不想更新您的代码,另一种方法是接收 SecureRandom 作为构造函数参数,但在测试端完成您想要的事情并不那么简单。

您可以制作一个 class 来扩展您的原始 class 并且只覆盖您想要的方法,例如:

class DummyClass(x: Int) {
  def doSomething(): String = "xyz"

  def doSomethingElse(): Int = x * 2
}

class DummyClass2(x: Int) extends DummyClass(x) {
  override def doSomething(): String = "abc"
}

val a = new DummyClass(3)
val b = new DummyClass2(3)

println(a.doSomething())     // xyz
println(a.doSomethingElse()) // 6
println(b.doSomething())     // abc
println(b.doSomethingElse()) // 6

(see live here)

或:

class DummyClass(x: Int) {
  def doSomething(): String = "xyz"

  def doSomethingElse(): Int = x * 2
}

object DummyClass2 extends DummyClass(3) {
  override def doSomething(): String = "abc"
}

val a = new DummyClass(3)
val b = DummyClass2

println(a.doSomething())     // xyz
println(a.doSomethingElse()) // 6
println(b.doSomething())     // abc
println(b.doSomethingElse()) // 6

(see live here)

但这并不理想...您遇到了随机代码的主要问题之一 - 难以测试。一种解决方案是将随机生成器放入新的 class/object/whatever 和 extend/inject 中,这样您就可以在测试中覆盖它。像这样:

import javax.inject.Inject

import scala.util.Random

class Generator {
  def rand = Random.nextInt()
}

class Dummy @Inject()(num: Int, gen: Generator) {
  def doSomething(): Int = num * gen.rand
}

// ---------------
//   ACTUAL CODE
// ---------------

val generator = new Generator
val x = new Dummy(3, generator)
println(x.doSomething()) // something
println(x.doSomething()) // something different

// ---------------
//    TEST CODE
// ---------------

val nonRandomGenerator = new Generator {
  override def rand: Int = 4
}
val y = new Dummy(3, nonRandomGenerator)
println(y.doSomething()) // 12
println(y.doSomething()) // 12