scala 单元测试_超越单元测试:Scala中的财产和法律测试简介

scala 单元测试

使您的单元测试更上一层楼。 (Taking your unit tests to the next level.)

I have been using ScalaCheck testing library for at least 2 years now. It allows you to take your unit tests to the next level.

我已经使用ScalaCheck测试库至少两年了。 它使您可以将单元测试提高到一个新的水平。

  • You can do

    你可以做

    Property-based testing by generating a lot of tests with random data and asserting properties on your functions. A simple code example is described below.

    通过基于随机数据生成大量测试并在函数中声明属性来进行基于属性的测试 。 下面描述一个简单的代码示例。

  • You can do Law testing that is even more powerful and allows you to check mathematical properties on your types.

    您可以进行功能更强大的Law测试 ,并可以检查类型的数学属性。

基于属性的测试 (Property-based testing)

Here is our beloved User data type:

这是我们钟爱的User数据类型:

case class User(name: String, age: Int)

And a random User generator:

以及一个随机的User生成器:

import org.scalacheck.{ Gen, Arbitrary }
import Arbitrary.arbitrary
implicit val randomUser: Arbitrary[User] = Arbitrary(for {
  randomName <- Gen.alphaStr
  randomAge  <- Gen.choose(0,80)
  } yield User(randomName, randomAge))

We can now generate a User like this:

我们现在可以生成一个这样的User

scala> randomUser.arbitrary.sample
res0: Option[User] = Some(User(OtwlaaxGbmdhuorlmgvXitbmGfbgetm,22))

Let’s define some functions on the User:

让我们在User上定义一些功能:

def isAdult: User => Boolean = _.age >= 18
def isAllowedToDrink : User => Boolean = _.age >= 21

Let’s claim that:

让我们声称:

All adults are allowed to drink.

允许所有成年人喝酒。

Can we somehow prove this? Is this correct for all users?

我们能以某种方式证明这一点吗? 这对所有用户都正确吗?

This is where property testing comes to the rescue. It allows us not to write specific unit-tests. Here they would be:

这是进行性能测试的地方。 它使我们不必编写特定的单元测试。 他们将是:

  • 18-year-olds are not allowed to drink

    禁止18岁以下的人喝酒
  • 19-year-olds are not allowed to drink

    19岁以下的人不允许喝酒
  • 20-year-olds are not allowed to drink

    不允许20岁以下的人喝酒

All of these statements can be replaced by a single property check:

所有这些语句都可以由单个属性检查代替:

import org.scalacheck.Prop.forAll
val allAdultsCanDrink = forAll { u: User =>
    if(isAdult(u)) isAllowedToDrink(u) else true }

Let’s run it:

让我们运行它:

scala> allAdultsCanDrink.check()
! Falsified after 0 passed tests.
> ARG_0: User(,19)

It fails as expected for a 19-year-old.

失败的原因是19岁。

Property testing is awesome for a few reasons:

性能测试非常棒,原因如下:

  • Saves time by writing less specific tests

    通过编写较少特定的测试来节省时间
  • Finds new use cases generated by Scala check that you forgot to handle

    查找您忘记处理的Scala检查生成的新用例
  • Forces you think in a more general way

    迫使您以更一般的方式思考
  • Gives you more confidence for refactoring than conventional unit tests

    比常规单元测试给您更多的重构信心

法律测试 (Law testing)

It gets better: let’s take it to the next level and define an Ordering between Users:

它会变得更好:让我们进入下一个级别,并定义用户之间的订购:

import scala.math.Orderingimplicit val userOrdering: Ordering[User] = Ordering.by(_.age)

We want to make sure that we didn't forget any edge cases and that we defined our order properly. This property has a name, and it’s called a total order. It needs to holds for the following properties:

我们要确保我们不会忘记任何边缘情况 ,并且我们正确定义了订单。 此属性有一个名称,称为总订单。 它需要保留以下属性:

  • Totality

    合计

  • Antisymmetry

    反对称

  • Transitivity

    传递性

Can we somehow prove this? Is this correct for all users?

我们能以某种方式证明这一点吗? 这对所有用户都正确吗?

This is possible without writing a single test!

无需编写单个测试就可以!

We use cats-laws library to define the laws we want to test on the ordering we defined:

我们使用cats-laws库来定义要根据定义的顺序测试的法律:

import cats.kernel.laws.discipline.OrderTests
import cats._
import org.scalatest.FunSuite
import org.typelevel.discipline.scalatest.Discipline
import org.scalacheck.ScalacheckShapeless._
class UserOrderSpec extends FunSuite with Discipline  {
//needed boilerplate to satisfy the dependencies of the framework
  implicit def eqUser[A: Eq]: Eq[Option[User]] = Eq.fromUniversalEquals
//convert our standard ordering to a `cats` order 
  implicit val catsUserOrder: Order[User] = Order.fromOrdering(userOrdering)
  
//check all mathematical properties on our ordering
  checkAll("User", OrderTests[User].order)
}

Let’s run it:

让我们运行它:

scala> new UserOrderSpec().execute()
UserOrderSpec:
- User.order.antisymmetry *** FAILED ***
  GeneratorDrivenPropertyCheckFailedException was thrown during property evaluation.
   (Discipline.scala:14)
    Falsified after 1 successful property evaluations.
    Location: (Discipline.scala:14)
    Occurred when passed generated values (
      arg0 = User(h,17),
      arg1 = User(edsb,17),
      arg2 = org.scalacheck.GenArities$$Lambda$2739/1277317528@41d7b4cf
    )
    Label of failing property:
      Expected: true
  Received: false
- User.order.compare
- User.order.gt
- User.order.gteqv
- User.order.lt
- User.order.max
- User.order.min
- User.order.partialCompare
- User.order.pmax
- User.order.pmin
- User.order.reflexitivity
- User.order.reflexitivity gt
- User.order.reflexitivity lt
- User.order.symmetry
- User.order.totality
- User.order.transitivity

Sure enough, it fails on the antisymmetry law! Same age and different names are not supposed to be equals. We forgot to use the name in our original Ordering, so let's fix it and rerun the laws:

果然,它不符合反对称定律! 相同的年龄和不同的名字不应该相等。 我们忘记了在原始Ordering使用该名称,因此让我们对其进行修复并重新执行法律:

implicit val userOrdering: Ordering[User] = Ordering.by( u => (u.age, u.name))
scala> new UserOrderSpec().execute()
UserOrderSpec:
- User.order.antisymmetry
- User.order.compare
- User.order.gt
- User.order.gteqv
- User.order.lt
- User.order.max
- User.order.min
- User.order.partialCompare
- User.order.pmax
- User.order.pmin
- User.order.reflexitivity
- User.order.reflexitivity gt
- User.order.reflexitivity lt
- User.order.symmetry
- User.order.totality
- User.order.transitivity

And now it passes :)

现在它通过了:)

If you are wondering what can you test besides Orders, go check out the docs here: https://typelevel.org/cats/typeclasses/lawtesting.html

如果您想知道除Order之外还可以测试什么,请在此处查看文档: https : //typelevel.org/cats/typeclasses/lawtesting.html

摘要 (Summary)

  • Property tests are more powerful than unit tests. They allow us to define properties on functions and generate a large number of tests using random data generators.

    属性测试比单元测试更强大。 它们使我们能够定义函数的属性,并使用随机数据生成器生成大量测试。
  • Law testing takes it to the next level and uses the mathematical properties of structures like Order to generate the properties and the tests.

    法律测试将其带入一个新的水平,并使用诸如Order之类的结构的数学特性来生成特性和测试。

  • Next time you define an ordering and wonder if it’s well-defined, go ahead and run the laws on it!

    下次您定义一个命令并想知道它是否定义明确时,请继续执行该规则!

翻译自: https://www.freecodecamp.org/news/beyond-unit-tests-an-intro-to-property-and-law-testing-in-scala-dd6a15898a19/

scala 单元测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值