【问题标题】:Replace a method for usage only in a particular unit tests in Scala替换仅在 Scala 中特定单元测试中使用的方法
【发布时间】:2019-10-14 12:08:55
【问题描述】:

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

这是我的场景: 我有方法

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 返回一个常量,而不是随机数。

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

【问题讨论】:

  • 你能显示更多代码吗? getSalt 来自哪里?是不是和getEncodedKey同班?

标签: scala scalatest


【解决方案1】:

一种简单的方法是创建一个像SaltGenerator 这样的特征,然后实现您的方法使用的特征,这将允许您创建一个SatlGenerator,它在测试时返回一个常量。

例如:

trait SaltGenerator {
  def getSalt(length): String
}

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

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

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

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

【讨论】:

    【解决方案2】:

    您可以创建一个扩展原始类的类,并且只覆盖您想要的方法,例如:

    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)

    但这并不理想...您遇到了随机代码的主要问题之一 - 很难测试。一种解决方案可能是将您的随机生成器放在一个新的类/对象/任何东西中并扩展/注入它,这样您就可以在测试中覆盖它。像这样的:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-10
      • 1970-01-01
      • 2020-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多