Im working with SBT and Play! Framework. Currently we have a commit stage in our pipeline where we publish to artifactory our binaries. The binaries are generated with the dist task. The pipeline then runs smoke and acceptance tests that are written in scala. They are run with sbt.
What I want to do is to compile the smoke and acceptance tests as well as the binary and publish them to artifactory. That will allow the pipeline to download these binaries (the test suites) and run them, instead of recompiling them every time, which takes a long time.
I tried sbt test:compile which generates the jar, but then I cant find a way to run the tests.
sbt dont publish test in artifacts
publishArtifact in GlobalScope in Test:== false
source: https://github.com/sbt/sbt/blob/a7413f6415687f32e6365598680f3bb8545c46b5/main/src/main/scala/sbt/Defaults.scala#L1118
this is how to enable it
// enable publishing the jar produced by `test:package`
publishArtifact in (Test, packageBin) := true
// enable publishing the test API jar
publishArtifact in (Test, packageDoc) := true
// enable publishing the test sources jar
publishArtifact in (Test, packageSrc) := true
source: http://www.scala-sbt.org/release/docs/Detailed-Topics/Artifacts
run the test
scala -classpath pipeline.jar classpath scalatest-<version>.jar org.scalatest.tools.Runner -p compiled_tests
where pipeline.jar is the test artifact you receive from the pipeline
or you can setup a test projet via sbt
http://www.scala-sbt.org/release/docs/Detailed-Topics/Testing.html
Related
I am looking to create fat jar by 'sbt assembly' but i dont wan to run integration test in sbt assembly. I want only jar file.
You can either disable tests in your build.sbt using assembly / test := {} as suggested by #laughedelic, or if you're executing from a terminal and want to skip to the tests for that build use: sbt 'set test in assembly := {}' assembly.
If you have set the value in your build.sbt file you can check that its value is as expected by running sbt show assembly / test which should return something like ().
I splitted my Unit- and Integration-Tests with a Filter:
lazy val FunTest = config("it") extend Test
def funTestFilter(name: String): Boolean = name endsWith "Spec"
def unitTestFilter(name: String): Boolean = name endsWith "Test"
...
testOptions in Test := Seq(Tests.Filter(unitTestFilter)),
testOptions in FunTest := Seq(Tests.Filter(funTestFilter)),
...
So I can do something like that:
sbt clean coverage test dockerComposeUp it:test dockerComposeStop coverageReport
Sadly that kills all my Coverage, only the generated BuildInfo has a Coverage.
Using only sbt clean coverage test coverageReport or sbt clean coverage it:test coverageReport work as expected.
The whole project can be found here: https://github.com/pme123/play-binding-form
scoverage Version: 1.5.1
SBT supports incremental compilation, but Scoverage does not support it. Scoverage clears instrumentation information before compilation starts and starts instrumentation process from scratch every time. Compilation of a subset of all classes with Scoverage enabled will result in wrong coverage reports.
In this case sbt-buldinfo plugin is enabled in server module. It registers source generator, which is executed before every compilation and generates server/target/scala_2.12/src_managed/main/sbt-buildinfo/BuildInfo.scala file.
SBT BuildInfo plugin is smart enough to regenerate this file only when its content changes, but since BuildInfoOption.BuildTime is included in buildInfoOptions setting,
this file is regeneraged before every compilation.
When it comes to compilation process, compiler finds one modified file (BuildInfo.scala) every time and starts incremental compilation of this one file. Scoverage clears its previous instrumentation information and saves only information about BuildInfo.scala file.
In case of execution like sbt clean coverage test dockerComposeUp it:test dockerComposeStop coverageReport the first compilation process is part of test task, and the second one it:test task. That's why there is no problem, when they are used separately.
Docker has nothing to do with our problem.
To fix the problem you have to prevent from BuildInfo.scala file regeneration on every compilation, at least when coverage is enabled.
I did it by modifying project/Settings.scala file in this way:
private lazy val buildInfoSettings = Seq(
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion),
buildInfoOptions ++= { if (coverageEnabled.value) Seq() else Seq(BuildInfoOption.BuildTime) }, // <-- this line was changed
buildInfoOptions += BuildInfoOption.ToJson,
buildInfoPackage := "pme123.adapters.version"
)
buildInfoOptions does not include BuildTime option when coverage is turned on.
It doesn't look elegeant, but it works. You can probably find better way.
instead of having different buildinfo objects depending on the phase, which could lead to compilation errors, you can use your own build time.
lazy val buildTime: SettingKey[String] = SettingKey[String]("buildTime", "time of build")
ThisBuild / buildTime := ZonedDateTime.now(ZoneOffset.UTC).toString
buildInfoKeys :=
Seq[BuildInfoKey](
name,
version,
scalaVersion,
sbtVersion,
buildTime
)
This should resolve this issue.
I have this configuration in a project of mine because I wanted a better control over the way the date is formatted, and I don't have the same issue
I am currently running a akka application with the below command after I run
sbt assembly
java -Dconfig.resource=/application.test.conf -cp /path/to/folder:./target/scala-2.11/app-name.jar ca.path.to.main
Is there a way I can pass this information using sbt and some flags so I don't have to run the sbt assembly task everytime just to run the application?
sbt run config=/application.test.conf cp=/path/to/folder:
(something like the above)
Options that are passed to JVM are read by sbt from the javaOptions setting. So you can configure this setting to have the options you want and then tell sbt to fork new JVM process everytime you run your app from sbt so these options are applied. You can do this from sbt console:
set javaOptions += "-Dconfig.resource=/application.test.conf"
set fork := true
run
Or in your build.sbt file:
javaOptions += "-Dconfig.resource=/application.test.conf"
fork := true
However this might not be the most idiomatic approach to reach your underlying end goal.
As part of a CI setup the very first step is to create a package/jar using SBT dist. The following steps are to run unit, integration and functional tests with the dist-created jar.
Is it possible to do that with SBT?
Normally I use sbt testOnly "Unit.*", but that works in the context of a project. I can't find any documentation showing how to do this when there is already a jar.
I'm using ScalaTest and I know there is a runner for it I could use http://www.scalatest.org/user_guide/using_the_runner. But using SBT would be simpler, if that is possible.
As an example, something like this is what I am looking for:
sbt testOnly "Unit.* -jar myjar.jar"
Will my tests even be included in the jar when I use the following:
sbt dist
?
EDIT
I created a new folder
I added build.sbt with the following content:
name := "abc"
version := "1.0-SNAPSHOT"
scalaVersion := "2.10.0"
I added the lib folder and copied my tests jar into it
I ran sbt testOnly Unit.*
It could not find any tests
EDIT 2
I tried with the following "proper" SBT file:
name := "ihs2tests"
version := "1.0-SNAPSHOT"
scalaVersion := "2.10.0"
unmanagedBase in Test := new java.io.File(".")
and moved test.jar into the root of the project. Again, no tests found.
Normally I use sbt testOnly "Unit.*", but that works in the context of a project. I can't find any documentation showing how to do this when there is already a jar.
The test-family tasks in SBT (with testOnly as an example) work with compile task that returns a list of compiled files through an sbt.inc.Analysis instance. I couldn't figure out how to amend it and inject the changed Analysis instance to testOnly so it knows the test I'm going to run exists.
I'm proposing another solution - a trick.
Package the tests classes to a jar with test:package task as follows:
[test-lib]> test:package
[info] Updating {file:/Users/jacek/sandbox/so/test-lib/}test-lib...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Scala source to /Users/jacek/sandbox/so/test-lib/target/scala-2.10/test-classes...
[info] Packaging /Users/jacek/sandbox/so/test-lib/target/scala-2.10/test-lib_2.10-0.1-SNAPSHOT-tests.jar ...
[info] Done packaging.
[success] Total time: 9 s, completed Mar 4, 2014 11:34:13 PM
When you have the test jar, you can execute the test framework on the command line without SBT (I assume you use ScalaTest given the scalatest tag, but I'll use Specs2). Read the documentation of the test framework on how to do it and for Specs2 it's specs2.run as described in Console output.
Executing tests from the test jar requires defining a proper CLASSPATH that may or may not be easy to get right. That's where SBT can be of great help - to manage dependencies and hence the CLASSPATH.
Create another SBT project and save the test jar in lib subdirectory to have it on CLASSPATH (as described in Unmanaged dependencies).
Dependencies in lib go on all the classpaths (for compile, test, run, and console).
Add a sample build.sbt where you define your test framework as a dependency of the project. For Specs2 it's as follows (I used the default configuration as described in the Specs2 home page):
libraryDependencies += "org.specs2" %% "specs2" % "2.3.8" % "test"
scalacOptions in Test ++= Seq("-Yrangepos")
The trick is to execute the main class of the test framework, e.g. specs2.run for Specs2, as if the class were executed on the command line. SBT helps with test:runMain.
[my-another-project]> test:runMain specs2.run HelloWorldSpec
[info] Running specs2.run HelloWorldSpec
HelloWorldSpec
The 'Hello world' string should
+ contain 11 characters
+ start with 'Hello'
+ end with 'world'
Total for specification HelloWorldSpec
Finished in 62 ms
3 examples, 0 failure, 0 error
Exception: sbt.TrapExitSecurityException thrown from the UncaughtExceptionHandler in thread "run-main-0"
[success] Total time: 5 s, completed Mar 4, 2014 11:15:14 PM
Don't worry about this Exception as it comes from SBT that catches exit from Specs2 (after test execution) so SBT remains up.
It seems that SBT can't read jar file without any additional/manual setup - I might be wrong but I didn't find anything in the docs. So I tried something like this to make the task simpler:
unzip some-test.jar
java -jar sbt-launch.jar \
'set scalaSource in Test := new java.io.File(".")' \
'set fullClasspath in Test += Attributed.blank(file("."))' \
'test'
This runs without errors but does not find tests.
If I add 'set includeFilter in (Test, unmanagedSources) := "*Suite*.class"' to force it to find tests it obviously fails because it expects *.scala files, not the compiled *.class files.
I'm not an SBT expert, but I think this must be close to a solution. There must be a way to read all files from a jar path programmatically and then tell test framework to use *.class files.
At this point is seems more reasonable to run tests using a Scalatest test runner or from sbt using the project.
If you wish to dig deeper take a look at SBT source code and the default sbt shell script that does lots of setup before it runs the sbt launcher jar.
For example, I need to do some pre-processing of source files before compiling. How do I ensure that sbt always runs this task before compile if compile was requested?
Here's what I do to make sure my tests are run before I publish locally:
publishLocal <<= publishLocal dependsOn (test in Test)
For you I think you'd need something like
compile <<= compile dependsOn (myTask in myContext)