Setting to get SBT to fail fast (stop) on an error - scala

I am running a multi-project SBT (v0.13) build and would like it to fail fast (stop) on the first error (compile) encountered in a sub-project.
The current behavior is that when something fails to compile in a sub-project, the build continues (to compile all the other sub-projects).
Is there a way/setting to make SBT stop and exit as soon as the first sub-project with a compile error is encountered?

In short, to my knowledge, no, SBT can not "fail fast" on a compiler or test error.
To my knowledge SBT doesn't control this. SBT is just going to invoke the appropriate test framework when it inspects your unit tests. The test framework can then decide what order to run the tests in, to run them concurrently, how to report issues, etc. Of course each test framework has its own features, configuration, and conventions. The two most widely used test frameworks for Scala are ScalaTest and Specs2.
Fortunately you can get the behavior you've requested in either Specs2 or ScalaTest. I've provided simple unit test examples below that fail early.
ScalaTest
You can get fail-fast behavior in ScalaTest for single test Suites by mixing in the CancelAfterFailure trait. For example, this test would execute the first test, fail the second, and show the third as cancelled.
class SomeSuite extends FunSuite with CancelAfterFailure with Assertions {
test("first") {
println("first")
}
test("second") {
assert(false)
}
test("third") {
println("third")
}
}
Specs2
Similar to ScalaTest you can control behavior in Specs2 on a per-Specification basis. To get fail-fast-like behavior you need to add two Arguments to your Specification: sequential and stopOnFail. See the docs for a full list of arguments you can set. (You do need both if you want an obvious linear ordering since Specs2 by default will execute your tests concurrently!)
class SomeSpec extends Specification {
sequential
stopOnFail
"specs2" should {
"first" in {
println("first")
ok
}
"second" in {
"foo" must equalTo ("bar")
}
"third" in {
println("third")
}
}
}

I've discovered that throwing a new java.lang.VirtualMachineError() {} halts the task immediately, while all other kinds of exceptions I have tried so far were swallowed.
I've tried ThreadDeath, InterruptedException, LinkageError, and ControlThrowable, and of course, the usual RuntimeException and such (the list came from scala.util.control.NonFatal)
<project root>/project/BuildUtils.scala
import sbt._
import sbt.Def._
object BuildUtils {
private def details(inc: Incomplete): Seq[Throwable] =
inc.directCause.toSeq ++ inc.causes.flatMap(details)
implicit class TaskSyntax[A](private val task: Initialize[Task[A]]) extends AnyVal {
def haltWhenFailed: Initialize[Task[A]] = task.result.map {
case Inc(cause) =>
throw new VirtualMachineError({
s"""Task has failed during execution.
|TaskNode: ${cause.node}
|DirectCause: ${details(cause).map(_.getMessage).distinct}
|""".stripMargin
}) {}
case Value(value) => value
}
}
}
<project root>/build.sbt
import BuildUtils._
lazy val ciBuild = taskKey[Unit]("Compile and run test")
ciBuild := {
val t1 # _ = (Test / compile).haltWhenFailed.value
val t2 # _ = (Test / test).haltWhenFailed.value
}
ciBuild will halt at first compilation error with rather verbose stacktrace and the specified message.

Related

How to use JUnit 5's #BeforeAll in Scala

Junit5 has introduced #BeforeAll to signal that the annotated method should be executed before all tests in the current test class. However it has requirement of method being static.
I have following structure in my Scala test suite:
class BaseTest extends MockitoSugar {
#BeforeAll
def codeNeedeForAllTests() = { <== this does not work as as it is not static
println("calling codeNeedeForAllTests")
// other code
}
}
and other test classes:
class IndexerTest extends BaseTest {
#Test
def add() = {
//test code
}
}
So I want that codeNeedeForAllTests get called before all tests, however the catch is #BeforeAll's requirement of method being static, for which I need to make codeNeedeForAllTests as object to have static method in Scala.
Now in Scala, a class can not extend an object and and object also can not extend object.
I also tried creating companion object of BaseTest, but that also did not work, any clean approach to do this?
#BeforeAll methods in JUnit Jupiter (a.k.a., JUnit 5) do not have to be static.
The link you provided is to JUnit 4.
This link is to JUnit Jupiter.
Excerpt:
Denotes that the annotated method should be executed before all #Test, #RepeatedTest, #ParameterizedTest, and #TestFactory methods in the current class; analogous to JUnit 4’s #BeforeClass. Such methods are inherited (unless they are hidden or overridden) and must be static (unless the "per-class" test instance lifecycle is used).
And that links to the documentation on Test Instance Lifecycle.
Thus, you should be able to annotate your test class with #TestInstance(Lifecycle.PER_CLASS) and declare your #BeforeAll method as non-static.
You can do this with FunCpec.
package net
import org.scalatest._
class Test extends FunSpec with BeforeAndAfterAll {
override def beforeAll: Unit = {
println("beforeAll")
}
describe("running a test") {
println("in describe")
it("should do something") {
println("in my test")
assert(true)
}
}
}
If u'll test it in REPL:
scala> test
[info] Compiling 1 Scala source to /Users/kmmere/Workspace/Work/scalatest_before/target/scala-2.11/test-classes...
in describe
beforeAll
in my test
[info] Test:
[info] running a test
[info] - should do something
[info] Run completed in 99 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 1 s, completed Oct 19, 2015 4:32:56 PM
Don't forget to include dependency of scalatest in yor build.sbt file:
"org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test
class FunSpec - facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.
Recommended Usage: For teams coming from Ruby's RSpec tool, FunSpec will feel familiar and comfortable; More generally, for any team that prefers BDD, FunSpec's nesting and gentle guide to structuring text (with describe and it) provide an excellent general-purpose choice for writing specification-style tests.

ScalaTest: Marking a scenario test as an expected failure

When working through testing with FeatureSpec and using scenario, I have part of my code that sends a "failure" case class (i.e. ApplesNotAllowed) and is used via a scalaz disjunction.
Quick example:
val result = if (order.value != 10) {
-\/(ApplesNotSupported(orderNumber))
} else {
\/-Option.empty[Orange]
}
In my test suite, I can easily test for the case where I receive an Option.empty[Orange] by expecting that it shouldBe None. However, I do run into a problem when I want to test for the ApplesNotSupported case class being returned. How would I expect this "failure" that will acknowledge the test as passing since we want the "failure" to happen? Whenever I try to run my scenario, I get the error:
Validation is not success ApplesNotSupported(orderNumber)
Tried using interrupt does not solve the issue since it is not an exception, nor do we want to deliberately fail the test. Could it be an issue with using scalaz and ScalaTest?
I am fairly new to functional programming and programming in Scala overall, so any resources or suggestions help as I do have trouble searching for documentation.
You may want to look in to using disjunction matchers when testing disjunctions:
https://github.com/typelevel/scalaz-scalatest/blob/master/src/main/scala/DisjunctionMatchers.scala
This is the best way to test based on the result of a disjunction.
You can match based on the disjunction itself:
must be_\/-
must be_-\/
Or go a step further and match on the result of the disjunction and the value it contains:
must be_\/-(value)
must be_-\/(value)
I don't personally use ScalaTest, but it looks like it should be the same as it is in specs2.
What about wrapping it in Try and do something like this:
import org.scalatest.{FunSuite, Matchers}
import org.specs2.mock.Mockito
import scala.util.{Failure, Success, Try}
/**
* Created by ssharma on 4/18/17.
*/
class UnitTest extends FunSuite with Matchers{
class A {
def t1(x: Int): Try[Int] = Try(t(x))
private def t(x: Int): Int = {
x match {
case 1 => 1
case _ => throw new RuntimeException
}
}
}
test("A") {
val b = new A
assert(b.t1(1)isSuccess)
}
test("A fail") {
val b = new A
assert(b.t1(2)isFailure)
}
}
I am not familiar with either scalaz or disjunctions but it sounds like the intercept functionality in scalatest's assertions (http://www.scalatest.org/user_guide/using_assertions) may help.
You define the type of exception you are expecting with intercept and you run the code that will throw the exception within the function. You can assign the result of intercept to a val which then you can compare to the expected value using assert or assertResult from scalatest.
Hope this can help! :)

Scalatest with eclipse shows errors while using Matchers

I have an eclipse scala project which uses maven. Eclipse plugins for ScalaIDE and Scalatest are installed. I have tests like:
import org.scalatest._
class ExampleSpec extends FlatSpec with Matchers {
feature("Feature A Test") {
scenario("Foo scenario 1") {
val a = FooClass().getResult()
a.count shouldBe 1 // IDE shows error: value shouldBe is not a member of Long
a(0).getString(0) shouldBe "FOO" // IDE shows error: value shouldBe is not a member of String
}
}
}
The maven compilation and the tests run fine, but in eclipse when I open this file, I see an error in eclipse wherever I am using a Matcher as mentioned in the comments above. Eg.
value shouldBe is not a member of Long
What am I missing? A scala test file shows hundreds of problems.
After adding the following dummy code:
case class Bar() {
def count = Array(Bar())
def getString(x: Int) = Array("aqq")
def apply[T](x: Int) = this
}
case class FooClass() {
def getResult() = Bar()
}
and changing FlatSpec to FeatureSpec as this is the syntax you are using in your ExampleSpec, the code compiles without any issues.
If it's still not the case for you I can suggest creating simple build.sbt and generating project with Eclipse sbt plugin.
I know this is old, but I had the same issue with eclipse (late 2018), and I was able to fix this by making sure the test was NOT in the default package. That is, add "package org.scalatest.examples.flatspec" to the beginning of your test, as an example, and move the test into that package.

Specify order of Specs2 Specification execution with SBT

I am trying to specify the order in which Spec2 Specifications are run, I know about the sequential keyword that makes sure they run one after another but this corresponds to the tests within a Specification (and doesn't actually guarantee any order)
I found this SO question: https://stackoverflow.com/a/15832297/1757402 which looked promising but again, seems to just sort the tests within a Specification
I am assuming SBT/Specs runs the Specifications in the order in which the JVM returns the classes, is there any way to change this? Or any way to guarantee an order?
So say I have the following Specifications:
CApplicationSpec.scala
#RunWith(classOf[JUnitRunner])
class CApplicationSpec extends Specification {
"CApplicationSpec" should {
"be OK" in new WithApplication{
OK must equalTo(OK)
}
}
}
BApplicationSpec
#RunWith(classOf[JUnitRunner])
class BApplicationSpec extends Specification {
"BApplicationSpec" should {
"be OK" in new WithApplication{
OK must equalTo(OK)
}
}
}
At the moment if I test these, the order of execution can change each time, I want a way to be able to guarantee that BApplication (or any other Spec) will always run first, maybe by sorting them alphabetically?
You can create a specification which will "link" other specifications and run them in a specified order:
object AllSpecifications extends Specification { def is = s2"""
${"run BSpecification" ~ BSpecification}
${"run CSpecification" ~ CSpecification}
"""
}
I ended up doing it via SBT with testGrouping
//Put all tests into a single Group so that SBT reports correct number of tests, but order them by test name
testGrouping in Test <<= definedTests in Test map { tests => {
val sortedTests = tests map {
test => new Tests.Group(test.name, Seq(test), Tests.InProcess)
} sortBy (_.name.toLowerCase) flatMap {
_.tests
}
Seq(new Tests.Group("Tests", sortedTests, Tests.InProcess))
}
}
Which orders all the tests alphabetically (including package name) I then have all specs I want to run first in a specific package

ScalaTest shared behaviour functions fail early

I'm trying to run tests by sharing behaviour in traits. When I run such a test, the test always aborts early when there's a failure, not running the rest of the tests.
Here's my test:
trait TestBehaviour extends ShouldMatchers {
def failEarly = fail("this fails before the test is run")
def failEarlyAgain = fail("this also fails before the test is run")
}
class Test extends FlatSpec with TestBehaviour {
it must behave like failEarly
it must behave like failEarlyAgain
}
If I run this in the ScalaTest IDE for Eclipse, no tests are ever shown - the RUN ABORTED text is just shown in the console:
*** RUN ABORTED ***
An exception or error caused a run to abort: this fails before the test is run
So it looks like the first test is being run early.
What I would expect to see is both the tests are run, and fail (with different reason messages).
change
def failEarly = fail("this fails before the test is run")
def failEarlyAgain = fail("this also fails before the test is run")
to
def failEarly() = fail("this fails before the test is run")
def failEarlyAgain() = fail("this also fails before the test is run")
In your version you are invoking the methods defined (because they don't have a parameter list). In my version you are passing a function created from the method (without invoking it)
It seems that the templated test, should contain an actual test-case; like so
import org.scalatest.FlatSpec
trait TestTemplate { this: FlatSpec =>
def failEarly() = {
it should "fail at the right moment" in {
fail("this fails once for each test that should behave like me")
}
}
}
class MyTest extends FlatSpec with TestTemplate {
"This Test" should behave like failEarly()
"Some other test" should behave like failEarly()
}
The example above seems to work for me.