How do I write a scala unit test that ensures compliation fails?

后端 未结 3 1823
别那么骄傲
别那么骄傲 2020-12-15 20:33

Is there any way to write something like a \"unit test\" which makes sure some code does not compile?

Why would I want such a thing? Two reasons.

1

相关标签:
3条回答
  • 2020-12-15 20:57

    Travis Brown's answer is absolutely correct. Just in the interest of completeness, I want to add that this also works for testing macros, as demonstrated here.

    One minor point: the illTyped check doesn't seem to work in the repl. It never throws an error, even if the given expression does type check. But don't let that fool you, it does work well.

    > test:console
    Welcome to Scala version 2.10.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_65).
    Type in expressions to have them evaluated.
    Type :help for more information.
    
    scala> def foo(s: String) = s
    foo: (s: String)String
    
    scala> import shapeless.test.illTyped
    import shapeless.test.illTyped
    
    scala> foo(1)
    <console>:10: error: type mismatch;
     found   : Int(1)
     required: String
                  foo(1)
                      ^
    
    scala> illTyped("""foo(1)""")
    
    scala> illTyped("""foo("hi there")""")  // <--- works, but shouldn't!
    
    0 讨论(0)
  • 2020-12-15 21:01

    As I note in a comment above, Shapeless 2.0 (not yet released but currently available as a milestone) has a very nice implementation of the functionality you're looking for, based on a solution by Stefan Zeiger. I've added a demo to your project here (note that I've had to update to Scala 2.10, since this solution uses a macro). It works like this:

    import shapeless.test.illTyped
    
    //this version won't even compile
    illTyped("getIdx(C.Ooga)")
    
    //We can have multiple enum unions exist side by side
    import Union_B_C._
    B.values().foreach {b => Union_B_C.getIdx(b) should be (b.ordinal())}
    C.values().foreach {c => Union_B_C.getIdx(c) should be (c.ordinal() + 2)}
    
    //Though A exists in some union type, Union_B_C still doesn't know about it,
    // so this won't compile
    illTyped("""
      A.values().foreach {a => Union_B_C.getIdx(a) should be (a.ordinal())}
    """)
    

    If we were to change the code in the second call to illTyped to something that will compile:

    B.values().foreach {a => Union_B_C.getIdx(a) should be (a.ordinal())}
    

    We'd get the following compilation error:

    [error] .../EnumUnionTest.scala:56: Type-checking succeeded unexpectedly.
    [error] Expected some error.
    [error]     illTyped("""
    [error]             ^
    [error] one error found
    [error] (core/test:compile) Compilation failed
    

    If you'd prefer a failed test, you could pretty easily adapt the implementation in Shapeless. See Miles's answer to my previous question for some addition discussion.

    0 讨论(0)
  • 2020-12-15 21:03

    Scalatest can also do this.

    Checking that a snippet of code does not compile

    Often when creating libraries you may wish to ensure that certain arrangements of code that represent potential “user errors” do not compile, so that your library is more error resistant. ScalaTest Matchers trait includes the following syntax for that purpose:

    "val a: String = 1" shouldNot compile
    

    If you want to ensure that a snippet of code does not compile because of a type error (as opposed to a syntax error), use:

    "val a: String = 1" shouldNot typeCheck
    

    Note that the shouldNot typeCheck syntax will only succeed if the given snippet of code does not compile because of a type error. A syntax error will still result on a thrown TestFailedException.

    If you want to state that a snippet of code does compile, you can make that more obvious with:

    "val a: Int = 1" should compile
    

    Although the previous three constructs are implemented with macros that determine at compile time whether the snippet of code represented by the string does or does not compile, errors are reported as test failures at runtime.

    0 讨论(0)
提交回复
热议问题