Подходы к тестированию, что метод не доступен для типа - PullRequest
3 голосов
/ 12 марта 2011

Приведена иерархия типов для игры, которая четко различает, чей ход следующий:

trait Game
trait BlackToPlay extends Game {
  def move(p: BlackPiece, s: Square): Either[FinishedGame, WhiteToPlay]
}
trait WhiteToPlay extends Game {
  def move(p: WhitePiece, s: Square): Either[FinishedGame, BlackToPlay]
}

Можно ли сделать следующее важное утверждение, не прибегая к размышлениям?

"A game with white to play" should {
  "not allow black to play" in {
    // an instance of whiteToPlay should not 
    // have the `move(BlackPiece, Square)` method.
  }
}

РЕДАКТИРОВАТЬ: Моя попытка реализовать решение @ Martin не работает. Есть мысли о том, что здесь не так? Из отчета:

scala> class B() {
     |   def b(s: String) = s
     | }
defined class B

scala> val b = new B()
b: B = B@420e44

scala> b.c("")
<console>:8: error: value c is not a member of B
       b.c("")
         ^

scala> b match {
     |   case _: { def c(s: String) } => false
     |   case _ => true
     | }
warning: there were unchecked warnings; re-run with -unchecked for details
res7: Boolean = false

res7 должно быть верным, потому что b не должен совпадать с типом структуры { def c(s: String) }

Ответы [ 6 ]

5 голосов
/ 12 марта 2011

Вы не проверяете то, что система типов уже гарантирует. На самом деле система типов уже является проверкой определенных свойств вашей программы.

Далее вы можете проверить, что типы, которые у вас есть, гарантируют определенное свойство (например, ни один игрок не делает ход два раза подряд), но на данный момент такие вещи ограничены языками, такими как Agda и Coq.

3 голосов
/ 12 марта 2011

Предполагается, что BlackPiece не является подтипом WhitePiece:

WhiteToPlayInstance.move (BlackPiece, s) не должен компилироваться - это означает, что вы не можете написать тест для него. Система типов гарантирует, что вы не можете переместить BlackPiece на WhiteToPlay.

1 голос
/ 29 марта 2011

Вопрос сродни вопросу: если val f: (Boolean) => Int, как я могу проверить, что f("hello world") отклонено компилятором?

После небольшого разговора в Melbourne Scala User Group мой вопрос был подтвержден (yay).В конце концов, ограничение, которое я пытаюсь проверить, включено в дизайн и поэтому заслуживает проверки.

Берни Поуп предположил, что требуется механизм Автоматизированное доказательство теорем .@ daniel-c-sobral был достаточно любезен, чтобы упомянуть Agda и Coq в несколько ином контексте, и это действительно технологии ATP, которые могли бы доказать, что мое приложение было правильным.

Еще одно предложение заключалось в том, чтобы выполнить код, вызывающий нарушение, какСценарий и утверждают, что это не удается.Бедняга eval, если хотите.

1 голос
/ 15 марта 2011

Я знаю, что вам не нужно решение для отражения, но вы можете (если Scala 2.9 приемлемо) использовать новую динамическую черту, например:

class ReflectionDynamic[T <: AnyRef](t: T) extends Dynamic {
  def typed[A]: A = sys.error("doh");

  def applyDynamic(name: String)(args: Any*) = {
    val argRefs = args.map {
      case a: AnyRef => a
      case _ => sys.error("only AnyRefs")
    }
    t.getClass.getMethod(name, argRefs.map(_.getClass): _*).invoke(t, argRefs: _*)
  }
}

... и это даст положительный тест:

val dynamicWhiteToPlay = new ReflectionDynamic(whiteToPlay)
dynamicWhiteToPlay.move(new WhitePiece, new Square) must_== Right(blackToPlay)

... и это отрицательно:

dynamicWhiteToPlay.move(new BlackPiece, new Square) must throwA[NoSuchMethodException]
1 голос
/ 13 марта 2011

Если вы действительно хотите проверить подобные вещи, переместите проверку с проверки типа на что-то динамическое. Предположим, что WhitePiece и BlackPiece имеют общий супертип Piece:

trait Game {
  def move(p : Piece, s : Square) : Either[FinishedGame, WhiteToPlay]
}

trait BlackToPlay extends Game
trait WhiteToPlay extends Game

Тогда тест может выглядеть так:

val b2p : BlackToPlay = ...
val bp : BlackPiece = ...
val wp : WhitePiece = ...
{a move bp} must not produce [IllegalMoveException]
{a move wp} must produce [IllegalMoveException]

Я не уверен, что это будет хороший дизайн, но это делает вашу систему явно тестируемой.

1 голос
/ 12 марта 2011

РЕДАКТИРОВАТЬ: Как указал Томас, приведенный ниже ответ является бессмысленным, поскольку структурные типы не могут использоваться в сопоставлениях с образцами в версии scala для JVM.


При обычных обстоятельствах это не имеет особого смысла, потому что scala статически типизирован и подобные вещи позаботятся компилятором, но если вы действительно широко используете отражение или структурную типизацию в своем коде, это может быть хорошим тестом:

instance match {
  case x: { def move(p: BlackPiece, s: Square): Either[FinishedGame, WhiteToPlay] } => // error
  case _ => // no error
}
...