2

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!

Piotr Kukielka
  • 3,792
  • 3
  • 32
  • 40
user1137701
  • 101
  • 3
  • 1
    You can looks at documentation of specs2: http://etorreborre.github.com/specs2/ – Piotr Kukielka Jan 10 '12 at 15:10
  • Assuming you have looked at the specs2 docs. Given your Board class, you want to confirm by writing tests that your code behaves as you expect by calling methods and checking the state or returned values are as you expect. Have a look here for some examples https://github.com/mongodb/casbah/tree/master/casbah-gridfs/src/test/scala Also look at this video http://www.youtube.com/watch?v=lMyNRUuEvNU – foolshat Jan 10 '12 at 15:26
  • Thank you, I took a look, but still don't get it! Can you pls give me an example for the board class? class BoardSpec extends SpecificationWithJUnit { } – user1137701 Jan 10 '12 at 15:39

2 Answers2

4

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!

Eric
  • 15,494
  • 38
  • 61
  • Hey Eric, thank you very much! How can I start this code? I tried to run a JUNIT-Test, but then get the result, that no JUNIT tests found – user1137701 Jan 11 '12 at 11:07
  • I'm using Intellij which supports specs2 directly. If you're using eclipse you need to extend org.specs2.SpecificationWithJUnit (if that doesn't work, you need to add the @RunWith(classOf[JUnitRunner]) annotation (JUnitRunner is in the org.specs2.runner package)) – Eric Jan 11 '12 at 20:49
  • Hey Eric, I tried to use this, but then get this error: java.lang.ClassCastException: TestSpec cannot be cast to org.scalatest.Suite – user1137701 Jan 17 '12 at 11:58
  • You need to run it as a specs2 test (or rewrite it for ScalaTest: www.scalatest.org). Which runner are you using? – Eric Jan 17 '12 at 20:42
-1

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.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • The thing is I can't throw away that code... The porgram is working but I need some test cases for it – user1137701 Jan 10 '12 at 15:48
  • @user1137701 If you are going to be a programmer, you'd better get used to throw away code and redo it. The class above is small enough that rewriting all of it should be easy enough. But, do what you want to do. I'm not going to change my advice because you don't want it. – Daniel C. Sobral Jan 10 '12 at 19:29