2017-02-03 14 views
2

Я использую ScalaCheck, чтобы провести некоторые тесты на основе свойств в ScalaTest. Скажем, я хочу проверить функцию, f(x: Double): Double, которая определена только для x >= 0.0 и которая возвращает NaN для аргументов вне этого домена. В идеале я хотел бы сделать что-то вроде этого:ScalaCheck действительная/недействительная граница теста

import org.scalatest.FunSpec 
import org.scalatest.prop.GeneratorDrivenPropertyChecks 

def f(x: Double) = Math.sqrt(x) // The actual function isn't important. 

class FTest 
extends FunSpec 
with GeneratorDrivenPropertyChecks { 
    describe("f(x)") { 
    it("must accept every argument value and handle it correctly") { 
     forAll { x: Double => 
     val r = f(x) 
     if(x >= 0.0) assert(!r.isNaN && r === Math.sqrt(x)) // Too simplistic, I know. ;-) 
     else assert(r.isNaN) 
     } 
    } 
    } 
} 

Теперь, это довольно элегантный и работает, но я обеспокоен пограничной проверки, потому что я сомневаюсь, что - в общем случае - ScalaCheck будет иметь возможность найти границу и проверить, что функция правильно отвечает значениями с обеих сторон этой границы (> = 0.0 в этом случае). Конечно, я могу разделить эти два условия с помощью whenever (ScalaTest 'замены s для ScalaCheck' ==> оператора s), но это больше усилий и много сгенерированных значений потрачены впустую:

class FTest2 
extends FunSpec 
with GeneratorDrivenPropertyChecks { 
    describe("f(x)") { 
    it("must accept every valid argument value and handle it correctly") { 
     forAll { x: Double => 
     whenever(x >= 0.0) { 
      val r = f(x) 
      assert(!r.isNaN && r === Math.sqrt(x)) 
     } 
     } 
    } 
    it("must report the correct error value for invalid argument values") { 
     forAll { x: Double => 
     whenever(x < 0.0) assert(f(x).isNaN) 
     } 
    } 
    } 
} 

(I знаю, что я также могу использовать генератор клиентов, чтобы ограничить диапазон, чтобы whenever не требовался, но я думаю, что это не относится к делу. Не стесняйтесь исправить меня, если я ошибаюсь.)

Итак, что я Любопытно:

  1. Есть ли способ намекнуть на Scala Проверьте, что такое граничное значение, и убедитесь, что он выбирает это значение и значения с любой стороны?
  2. Есть ли альтернатива этому, что в равной степени элегантно, но что лучше подходит для поиска границы автоматически?

Спасибо за вашу помощь ~

ответ

1

ScalaCheck не может автоматически выяснить, какие значения вашей функция относится к действительной; вам нужно либо закодировать эту информацию в своих свойствах (используя что-то вроде whenever), либо в ваших генераторах. Какой подход к выбору контекстно-зависим.

Сохранение свойств «маленький» является предпочтительным: сфокусированные, ортогональные свойства легче читать/писать/обслуживать, и вы всегда можете составить их позже для создания более полных свойств. Поэтому я бы сохранил два свойства (счастливые и несчастные случаи) отдельно.

Чтобы избежать «истощения» генерируемых значений, я использовал бы два отдельных генератора (один для неотрицательных удвоений и другой для отрицательных удвоений); нет необходимости в whenever с этим подходом.

val genNonnegativeDouble: Gen[Double] = Gen.choose(0, Double.MaxValue) 

val genNegativeDouble: Gen[Double] = Gen.negNum[Double] 

Ваши свойства будут выглядеть следующим образом:

final class FTest2 
    extends FunSpec 
    with GeneratorDrivenPropertyChecks { 

    describe("f") { 
    it("must accept every valid argument value and handle it correctly") { 
     forAll(genNonnegativeDouble) { x => 
      val r = f(x) 
      assert(!r.isNaN && r === Math.sqrt(x)) 
     } 
    } 

    it("must report the correct error value for invalid argument values") { 
     forAll(negativeDouble) { x => 
     assert(f(x).isNaN) 
     } 
    } 
    } 

} 

Кстати,

  • Вы должны, вероятно, вернуть что-то другое, чем Double.NaN, чтобы указать отказ; Option[Double] - хороший кандидат, здесь, поскольку есть только одна причина неудачи.
  • Вы должны только проверить двухместный номер для приблизительно равенства.
+2

Спасибо за ответ. Наверное, ты не говоришь мне ничего, чего я еще не знал. Я надеялся, что есть простой и сжатый способ одновременной проверки ответа функции как на хороший, так и на плохой ввод, путем информирования _ScalaCheck_, где входные границы где. Что касается 'Double.NaN', это то, что возвращает Math.sqrt при заданных аргументах за пределами своего домена - я бы никогда не использовал это в своем собственном коде. :-) Поскольку целью было сосредоточиться на граничных условиях, я также не слишком беспокоился об использовании допусков для проверки значений «Double». – scalabling

+1

Кстати, 'oneOf' выберет 0.0 в половину времени. 'posNum' и' negNum' выбирают только целочисленные значения. Я думаю, что 'select' станет лучшей основой для пользовательского генератора в этом случае. – scalabling

+0

@scalabling True. Я поправлю свой ответ соответственно. – Jubobs