【问题标题】:How to unit-test different typeclass instances for Numeric?如何对 Numeric 的不同类型类实例进行单元测试?
【发布时间】:2019-04-25 18:56:17
【问题描述】:

假设我有两个类型类 Numeric 的实例。

class Money(c: String, x: Long, y: Int)
class Quantity(c: String, x: Long, y: Int)
implicit val numericMoney: Numeric[Money] = new Numeric[Money]
implicit val numericQuantity: Numeric[Quantity] = new Numeric[Quantity]

Money 和 Quantity 在 Numeric 实例中的行为应该相同。 我有 scalaTest 测试来检查 Money 的行为是否正确。

例如

import implicits.NumericMoney.numericMoney._

class MoneyOpsSpec extends WordSpec with Matchers {

  val max = Money("", Long.MaxValue, 999999999)
  val min = Money("", Long.MinValue, -999999999)

  "A Money object" when {
    "zero" should {
      "be neutral element under addition" in {
        zero + Money("", 15, 50) should ===(Money("", 15, 50))
        Money("", 15, 50) + zero should ===(Money("", 15, 50))
      }
      "be neutral element under subtraction" in {
        zero - Money("", 15, 50) should ===(Money("", -15, -50))
        Money("", 15, 50) - zero should ===(Money("", 15, 50))
      }
      "be invariant under negation" in {
        -zero should ===(zero)
      }
    }
  }
}

Quantity 规范应该以相同的方式执行。我可以实现一个通用规范并使用MoneyQuantity 作为该规范的输入吗?或者 scalaTest 或 specs2 是否有一些东西可以确保 Numeric 类型类实例的行为正确?我可以轻松切换测试框架。

【问题讨论】:

    标签: scala typeclass scalatest specs2


    【解决方案1】:

    我可以实现一个通用规范并使用货币和数量作为该规范的输入吗?

    当然。只需将隐式作为构造函数参数即可。未经测试,但应该是近似的(变化很小):

    abstract class NumOpsSpec[T](implicit num: Numeric[T], tag: ClassTag[T]) extends WordSpec with Matchers {
      import num._
    
      val max: T
      val min: T
      val someElement: T
    
      s"A ${tag.runtimeClass.simpleName} object" when {
        "zero" should {
          "be neutral element under addition" in {
            zero + someElement should ===(someElement)
            someElement + zero should ===(someElement)
          }
          "be neutral element under subtraction" in {
            zero - someElement should ===(- someElement)
            someElement - zero should ===(someElement)
          }
          "be invariant under negation" in {
            -zero should ===(zero)
          }
        }
      }
    }
    
    class MoneyOpsSpec extends NumOpsSpec[Money] {
      override val max = Money("", Long.MaxValue, 999999999)
      override val min = Money("", Long.MinValue, -999999999)
      override val someElement = Money("", 15, 50)
    }
    
    class QuantityOpsSpec extends NumOpsSpec[Quantity] {
      override val max = ???
      override val min = ???
      override val someElement = ???
    }
    

    您也可以查看 https://github.com/typelevel/discipline 以测试一般的类型类法律。

    【讨论】:

      【解决方案2】:

      我认为您需要创建用于测试不同操作的抽象方法,并将其与两个对象一起使用。例如。测试加法

        def testAddition[T](a: T, b: T, expectedResult: T)(implicit n: Numeric[T]) = {
          n.plus(a, b) ==== expectedResult
        }
      

      那么你可以用Money或者Quantity调用这个方法

      testAddition(Money(1, 1), Money(2, 2), Money(3, 3))
      testAddition(Quantity(1, 1), Quantity(2, 2), Quantity(3, 3))
      

      【讨论】:

      • 我想避免这种方法,而是使用 WordMatchers 语法,但在规范中注入了不同的实例
      猜你喜欢
      • 2018-11-01
      • 2015-11-03
      • 2012-10-20
      • 2019-11-25
      • 2011-09-17
      • 2012-01-08
      • 2011-12-19
      • 2014-10-08
      • 2011-03-24
      相关资源
      最近更新 更多