scalatest "A stack" should "do something" -- wtf? How is should a method of string?

1.5k views Asked by At

I'm just starting out with scala here and I'm finding some of the syntax confusing. For example from the scalatest main page

  class ExampleSpec extends FlatSpec with Matchers {

    "A Stack" should "pop values in last-in-first-out order" in {...}
 }

As I read it that means "should" is a method of string "A stack"? If that is right, how does that happen? It doesn't seem to work from the scala prompt

 scala> class ExampleSpec {
 |    "A Stack" should "pop values"
 | }
 <console>:9: error: value should is not a member of String
      "A Stack" should "pop values"

If "should" is not a method of string "A Stack" in the above snippet then how am I to read the snippet correctly? What is "should" and how does it relate to the string? Any clues?

2

There are 2 answers

0
Akos Krivachy On

This is commonly known as the Pimp My Library Enrich My Library pattern where we can extend other libraries (in this case Scala strings) with our own methods by using implicit conversions.

The way it works is that FlatSpec mixes in a trait called ShouldVerb which has the following implicit conversion defined:

 implicit def convertToStringShouldWrapper(o: String): StringShouldWrapperForVerb =
   new StringShouldWrapperForVerb {
     val leftSideString = o.trim
   }

And StringShouldWrapperForVerb has the should method defined:

def should(right: String) ....

The reason it does not work in the REPL for you is that you don't have FlatSpec and via that the ShouldVerb trait mixed in.

You can read more about this in the documentation under implicit classes.

1
Ryan On

In org.scalatest.Matchers, an implicit conversion is provided that converts String to StringShouldWrapper, which is the class that provides the should function on String:

implicit def convertToStringShouldWrapper(o: String): StringShouldWrapper

The compiler will implicitly convert any String to a StringShouldWrapper when it encounters that call. If you call a function on an object that doesn't exist, the compiler will check in-scope implicit conversions (like the one above) to see if any of them will provide that function.