Scala 规范单元测试

发布于 2024-12-26 08:03:59 字数 1320 浏览 1 评论 0原文

我有以下课程,我想编写一些规范测试用例,但我对它真的很陌生,我不知道如何开始。我的课确实是这样的:

class Board{

  val array = Array.fill(7)(Array.fill(6)(None:Option[Coin]))

  def move(x:Int, coin:Coin) {
    val y = array(x).indexOf(None)
    require(y >= 0) 
    array(x)(y) = Some(coin)
   }

  def apply(x: Int, y: Int):Option[Coin] = 
     if (0 <= x && x < 7 && 0 <= y && y < 6) array(x)(y)
     else None

  def winner: Option[Coin] = winner(Cross).orElse(winner(Naught))

  private def winner(coin:Coin):Option[Coin] = {
    val rows = (0 until 6).map(y => (0 until 7).map( x => apply(x,y)))
    val cols = (0 until 7).map(x => (0 until 6).map( y => apply(x,y)))
    val dia1 = (0 until 4).map(x => (0 until 6).map( y => apply(x+y,y)))
    val dia2 = (3 until 7).map(x => (0 until 6).map( y => apply(x-y,y)))

    val slice = List.fill(4)(Some(coin))
    if((rows ++ cols ++ dia1 ++ dia2).exists(_.containsSlice(slice))) 
      Some(coin)
    else None
  }  

  override def toString = {
    val string = new StringBuilder
    for(y <- 5 to 0 by -1; x <- 0 to 6){
        string.append(apply(x, y).getOrElse("_"))
        if (x == 6) string.append ("\n") 
        else string.append("|")
    }
    string.append("0 1 2 3 4 5 6\n").toString
  }
}

谢谢!

I ve got the following class and I want to write some Spec test cases, but I am really new to it and I don't know how to start. My class do loke like this:

class Board{

  val array = Array.fill(7)(Array.fill(6)(None:Option[Coin]))

  def move(x:Int, coin:Coin) {
    val y = array(x).indexOf(None)
    require(y >= 0) 
    array(x)(y) = Some(coin)
   }

  def apply(x: Int, y: Int):Option[Coin] = 
     if (0 <= x && x < 7 && 0 <= y && y < 6) array(x)(y)
     else None

  def winner: Option[Coin] = winner(Cross).orElse(winner(Naught))

  private def winner(coin:Coin):Option[Coin] = {
    val rows = (0 until 6).map(y => (0 until 7).map( x => apply(x,y)))
    val cols = (0 until 7).map(x => (0 until 6).map( y => apply(x,y)))
    val dia1 = (0 until 4).map(x => (0 until 6).map( y => apply(x+y,y)))
    val dia2 = (3 until 7).map(x => (0 until 6).map( y => apply(x-y,y)))

    val slice = List.fill(4)(Some(coin))
    if((rows ++ cols ++ dia1 ++ dia2).exists(_.containsSlice(slice))) 
      Some(coin)
    else None
  }  

  override def toString = {
    val string = new StringBuilder
    for(y <- 5 to 0 by -1; x <- 0 to 6){
        string.append(apply(x, y).getOrElse("_"))
        if (x == 6) string.append ("\n") 
        else string.append("|")
    }
    string.append("0 1 2 3 4 5 6\n").toString
  }
}

Thank you!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

橘香 2025-01-02 08:03:59

我只能赞同 Daniel 的建议,因为通过使用 TDD,你最终会得到一个更实用的 API。

我还认为您的应用程序可以结合使用 specs2 和 ScalaCheck 进行很好的测试。这里是帮助您入门的规范草案:

  import org.specs2._
  import org.scalacheck.{Arbitrary, Gen}

  class TestSpec extends Specification with ScalaCheck { def is =

    "moving a coin in a column moves the coin to the nearest empty slot" ! e1^
    "a coin wins if"                                                     ^
      "a row contains 4 consecutive coins"                               ! e2^
      "a column contains 4 consecutive coins"                            ! e3^
      "a diagonal contains 4 consecutive coins"                          ! e4^
                                                                         end

    def e1 = check { (b: Board, x: Int, c: Coin) =>
      try { b.move(x, c) } catch { case e => () }
      // either there was a coin before somewhere in that column
      // or there is now after the move
      (0 until 6).exists(y => b(x, y).isDefined)
    }

    def e2 = pending
    def e3 = pending
    def e4 = pending

    /**
     * Random data for Coins, x position and Board
     */
    implicit def arbitraryCoin: Arbitrary[Coin]     = Arbitrary { Gen.oneOf(Cross,       Naught) }
    implicit def arbitraryXPosition: Arbitrary[Int] = Arbitrary { Gen.choose(0, 6) }
    implicit def arbitraryBoardMove: Arbitrary[(Int, Coin)]   = Arbitrary {
      for {
        coin <- arbitraryCoin.arbitrary
        x    <- arbitraryXPosition.arbitrary
      } yield (x, coin)
    }
    implicit def arbitraryBoard: Arbitrary[Board]   = Arbitrary {
      for {
        moves <- Gen.listOf1(arbitraryBoardMove.arbitrary)
      } yield {
        val board = new Board
        moves.foreach { case (x, coin) => 
          try { board.move(x, coin) } catch { case e => () }}
          board
      }
    }


  }

  object Cross extends Coin {
    override def toString = "x"
  }
  object Naught extends Coin {
    override def toString = "o"
  }
  sealed trait Coin

我实现的 e1 属性不是真实的,因为它并没有真正检查我们是否将硬币移至最近的 空槽,这是您的代码和 API 所建议的。您还需要更改生成的数据,以便通过 xo 的交替生成板。这应该是学习如何使用 ScalaCheck 的好方法!

I can only second Daniel's suggestion, because you'll end up with a more practical API by using TDD.

I also think that your application could be nicely tested with a mix of specs2 and ScalaCheck. Here the draft of a Specification to get you started:

  import org.specs2._
  import org.scalacheck.{Arbitrary, Gen}

  class TestSpec extends Specification with ScalaCheck { def is =

    "moving a coin in a column moves the coin to the nearest empty slot" ! e1^
    "a coin wins if"                                                     ^
      "a row contains 4 consecutive coins"                               ! e2^
      "a column contains 4 consecutive coins"                            ! e3^
      "a diagonal contains 4 consecutive coins"                          ! e4^
                                                                         end

    def e1 = check { (b: Board, x: Int, c: Coin) =>
      try { b.move(x, c) } catch { case e => () }
      // either there was a coin before somewhere in that column
      // or there is now after the move
      (0 until 6).exists(y => b(x, y).isDefined)
    }

    def e2 = pending
    def e3 = pending
    def e4 = pending

    /**
     * Random data for Coins, x position and Board
     */
    implicit def arbitraryCoin: Arbitrary[Coin]     = Arbitrary { Gen.oneOf(Cross,       Naught) }
    implicit def arbitraryXPosition: Arbitrary[Int] = Arbitrary { Gen.choose(0, 6) }
    implicit def arbitraryBoardMove: Arbitrary[(Int, Coin)]   = Arbitrary {
      for {
        coin <- arbitraryCoin.arbitrary
        x    <- arbitraryXPosition.arbitrary
      } yield (x, coin)
    }
    implicit def arbitraryBoard: Arbitrary[Board]   = Arbitrary {
      for {
        moves <- Gen.listOf1(arbitraryBoardMove.arbitrary)
      } yield {
        val board = new Board
        moves.foreach { case (x, coin) => 
          try { board.move(x, coin) } catch { case e => () }}
          board
      }
    }


  }

  object Cross extends Coin {
    override def toString = "x"
  }
  object Naught extends Coin {
    override def toString = "o"
  }
  sealed trait Coin

The e1 property I've implemented is not the real thing because it doesn't really check that we moved the coin to the nearest empty slot, which is what your code and your API suggests. You will also want to change the generated data so that the Boards are generated with an alternation of x and o. That should be a great way to learn how to use ScalaCheck!

咆哮 2025-01-02 08:03:59

我建议你扔掉所有代码——好吧,将其保存在某个地方,但使用 TDD 从零开始。

Specs2 网站有大量关于如何编写测试的示例,但使用 TDD(测试驱动设计)来完成。至少可以说,事后添加测试并不是最理想的。

因此,考虑一下您想要处理最简单功能的最简单情况,为此编写一个测试,看看它是否失败,然后编写代码来修复它。如有必要,请进行重构,并对下一个最简单的情况重复此操作。

如果您需要有关如何进行 TDD 的一般帮助,我衷心赞同 Clean Coders 上提供的有关 TDD 的视频。至少,请观看第二部分,鲍勃·马丁 (Bob Martin) 编写了一整堂 TDD 风格的课程,从设计到结束。

如果您知道如何进行一般测试,但对 Scala 或 Specs 感到困惑,请更具体地说明您的问题是什么。

I suggest you throw all that code out -- well, save it somewhere, but start from zero using TDD.

The Specs2 site has plenty examples of how to write tests, but use TDD -- test driven design -- to do it. Adding tests after the fact is suboptimal, to say the least.

So, think of the most simple case you want to handle of the most simple feature, write a test for that, see it fail, write the code to fix it. Refactor if necessary, and repeat for the next most simple case.

If you want help with how to do TDD in general, I heartily endorse the videos about TDD available on Clean Coders. At the very least, watch the second part where Bob Martin writes a whole class TDD-style, from design to end.

If you know how to do testing in general but are confused about Scala or Specs, please be much more specific about what your questions are.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文