I have a Play! 2 for Scala application, and I am using Specs2 for tests. I can run all tests with the test command, or a particular specification with test-only MyParticularSpec.
What I would like to do is mark some particular specifications, or even single methods inside a specification, in order to do things like:
running all tests that are not integration (that is, that do not access external resources)
running all tests that do not access external resources in write mode (but still running the reading tests)
running all tests but a given one
and so on.
I guess something like that should be doable, perhaps by adding some annotations, but I am not sure how to go for it.
Does there exist a mechanism to selectively run some tests and not other ones?
EDIT I have answered myself when using test-only. Still the command line option does not work for the test task. Following the sbt guide I have tried to create an additional sbt configuration, like
object ApplicationBuild extends Build {
// more settings
lazy val UnitTest = config("unit") extend(Test)
lazy val specs = "org.scala-tools.testing" %% "specs" % "1.6.9" % "unit"
val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA)
.configs(UnitTest)
.settings(inConfig(UnitTest)(Defaults.testTasks) : _*)
.settings(
testOptions in UnitTest += Tests.Argument("exclude integration"),
libraryDependencies += specs
)
}
This works when I pass arguments without options, for instance when I put Test.Argument("plan"). But I was not able to find how to pass a more complex argument. I have tried
Tests.Argument("exclude integration")
Tests.Argument("exclude=integration")
Tests.Argument("-exclude integration")
Tests.Argument("-exclude=integration")
Tests.Argument("exclude", "integration")
Tests.Argument("exclude \"integration\"")
and probably more. Still not any clue what is the correct syntax.
Does anyone know how to pass arguments with options to specs2 from sbt?
First, following the specs2 guide one must add tags to the specifications, like this
class MySpec extends Specification with Tags {
"My spec" should {
"exclude this test" in {
true
} tag ("foo")
"include this one" in {
true
}
}
}
The command line arguments to include are documented here
Then one can selectively include or exclude test with
test-only MySpec -- exclude foo
test-only MySpec -- include foo
You can also use without any change to your build
test-only * -- exclude integration
Tested in Play 2.1-RC3
If you want to pass several arguments you can add several strings to one Test.Argument
testOptions in Test += Tests.Argument("include", "unit")
There are examples of this in the specs2 User Guide here and in the Play documentation there.
I'm using Play2.2, and there are 2 ways to do this depending on whether or not you are in the play console.
From the console type: test-only full.namespace.to.TestSpec
From the terminal type: test-only "full.namespace.to.TestSpec"
I came across this question while trying to figure out how to do something similar for ScalaTest with Play. SBT has detailed documentation on how to configure additional test configurations but these could use a bit of tweaking for Play.
Apart from the subtly different Project configuration I found that I wanted to crib a bunch of the test settings from PlaySettings. The following is running and generating an Intellij project with integration test sources in the "/it" directory. I may still be missing reporting and lifecycle hooks,
object BuildSettings {
def testSettings = {
// required for ScalaTest. See http://stackoverflow.com/questions/10362388/using-scalatest-in-a-playframework-project
testOptions in Test := Nil
}
def itSettings = {
// variously cribbed from https://github.com/playframework/Play20/blob/master/framework/src/sbt-plugin/src/main/scala/PlaySettings.scala
sourceDirectory in IntegrationTest <<= baseDirectory / "it"
scalaSource in Test <<= baseDirectory / "it"
libraryDependencies += "play" %% "play-test" % play.core.PlayVersion.current % "it"
}
}
object ApplicationBuild extends Build {
val main = play.Project(
appName,
appVersion,
Dependencies.dependencies)
.configs( IntegrationTest )
.settings(Dependencies.resolutionRepos)
.settings(BuildSettings.testSettings)
.settings(Defaults.itSettings : _*)
.settings(BuildSettings.itSettings)
}
Related
I'm currently writing an sbt plugin. The tasks that I define are using functionality from libraries that are otherwise unrelated to sbt and that I don't want to change. These libraries use slf4j for logging. I would like the logging output to show up in the sbt console as if I had used streams.value.log, so that I can e. g. turn debug logging on and off in the usual sbt ways like set someTask / logLevel := Level.Debug. Is there a way to do this?
It seems like sbt-slf4j is supposed to solve my problem:
https://index.scala-lang.org/eltimn/sbt-slf4j/sbt-slf4j/1.0.4?target=_2.12
But I wasn't able to get it to work. My project/build.sbt looks like this:
libraryDependencies += "com.eltimn" %% "sbt-slf4j" % "1.0.4"
resolvers += Resolver.bintrayRepo("eltimn", "maven")
and build.sbt like so:
import org.slf4j.LoggerFactory
import org.slf4j.impl.SbtStaticLoggerBinder
val foo = taskKey[Unit]("foobar")
foo := {
SbtStaticLoggerBinder.sbtLogger = streams.value.log
// symbolic implementation, the actual implementation lives in a library
val logger = LoggerFactory.getLogger("foobar")
logger.warn("Hello!")
}
But running foo does not result in a warning being printed. A warning is printed if I change it to LoggerFactory.getLogger(streams.value.log.name), but this is not an option because again, the code lives in a library.
Is there any good way to solve this?
For a project based on Play with sbt, I'd like to have multiple flavors for test runs, using different configuration files. Motivation is being able to run tests either against a local or a remote database.
There's already a custom config file speicified for general test runs (in build.sbt):
javaOptions in Test += "-Dconfig.file=conf/application.test.conf"
Now I would like to have another command where the same tests run against some configuration file conf/application.test-ci.conf.
Approaches tried so far
Adding a command alias
addCommandAlias("test-ci", ";test -Dconfig.file=conf/application.test-ci.conf")
This fails with an error message of a missing semicolon (;), indicating that sbt interprets the resulting command line as multiple commands, but I don't understand why.
Extend Test
lazy val CITest = config("ci") extend Test
lazy val config = (project in file(".")).enablePlugins(PlayScala)
.configs(CITest)
.settings(inConfig(CITest)(Defaults.testTasks): _*)
.settings(
javaOptions in CITest += "-Dconfig.file=conf/application.test-ci.conf"
)
javaOptions in CITest -= "-Dconfig.file=conf/application.test.conf"
I'm don't fully understand what this is doing, but it always seems to pick up the other test configuration file.
How can I specify multiple test setups picking up different configuration files?
Try first applying the setting via set command and then follow up with test task like so
addCommandAlias(
"test-ci",
""";set Test/javaOptions ++= Seq("-Dconfig.file=conf/application.test.con"); test"""
)
Notice how ; separates the set from test.
Another approach is to modify the setting based on the environment. Usually there is some environment variable set on CI like CI or BUILD, so you can modify javaOptions conditionally (without any custom configurations):
Test/javaOptions ++= {
if (sys.env.get("CI").isEmpty) Seq.empty
else Seq("-Dconfig.file=conf/application.test-ci.conf")
}
Note: Test/javaOptions is the new syntax for javaOptions in Test (since sbt 1)
I'm attempting to compile and run scala code that generates a file in SBT (in this case its a swagger file).
the following sbt executes fine. The jar is built and executed and the swagger.zip file the execution creates is in the target directory. I can't, however, seem to get the zip file to get published to my artifactory repo like my standard jar files would.
Any idea on what I'm missing?
publishArtifact in (Compile, packageBin) := false
publishArtifact in run := true
val myZipTask = taskKey[File]("swagger-zip")
myZipTask := {
file("swagger.zip")
}
addArtifact(Artifact("swagger", "zip", "zip"), myZipTask )
The run command doesn't trigger any publishing tasks, as that's not what it's for. I think you have tried to turn on publishing when run is called with "publishArtifact in run := true", but that code is not sufficient to achieve that; you'd need to attach many other Tasks to that command. That's not what you want anyway.
Your case fits entirely within the expected usage of the addArtifact helper (see the docs, and the code)
As you need to run code to generate artefacts, you should provide that code as a Task argument to the addArtifact helper, i.e.
val myZipTask = taskKey[File]("return the swagger-zip file")
val runZipCodeTask = taskKey[Unit]("run the swagger-zip code")
// See http://www.scala-sbt.org/0.13.2/docs/faq.html, “How can I create a custom run task, in addition to run?”
// and https://stackoverflow.com/questions/23409993/defining-sbt-task-that-invokes-method-from-project-code
fullRunTask(runZipCodeTask, Compile, "ZipGeneratorMainClass")
myZipTask := {
runZipCodeTask.value
file("target/swagger/swagger.zip")
}
addArtifact(Artifact("swagger", "zip", "zip"), myZipTask)
Then run "sbt publish"
Working demo
See https://gist.github.com/RichardBradley/5384a5e0da2427df237f42fe512b30b8 for a working demo.
Note about scopes - consider generating this file in a meta-project, not the main project
See https://stackoverflow.com/a/23416018/8261
I would like to use scala-js with sbt-web in such a way that it can be compiled to produce javascript assets that are added to the asset pipeline (e.g. gzip, digest). I am aware of lihaoyi's workbench project but I do not believe this affects the asset pipeline. How can these two projects be integrated as an sbt-web plugin?
Scala-js produces js files from Scala files. The sbt-web documentation calls this a Source file task.
The result would look something like this:
val compileWithScalaJs = taskKey[Seq[File]]("Compiles with Scala js")
compileWithScalaJs := {
// call the correct compilation function / task on the Scala.js project
// copy the resulting javascript files to webTarget.value / "scalajs-plugin"
// return a list of those files
}
sourceGenerators in Assets <+= compileWithScalaJs
Edit
To create a plugin involves a bit more work. Scala.js is not yet an AutoPlugin, so you might have some problems with dependencies.
The first part is that you add the Scala.js library as a dependency to your plugin. You can do that by using code like this:
libraryDependencies += Defaults.sbtPluginExtra(
"org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5",
(sbtBinaryVersion in update).value,
(scalaBinaryVersion in update).value
)
Your plugin would look something like this:
object MyScalaJsPlugin extends AutoPlugin {
/*
Other settings like autoImport and requires (for the sbt web dependency),
see the link above for more information
*/
def projectSettings = scalaJSSettings ++ Seq(
// here you add the sourceGenerators in Assets implementation
// these settings are scoped to the project, which allows you access
// to directories in the project as well
)
}
Then in a project that uses this plugin you can do:
lazy val root = project.in( file(".") ).enablePlugins(MyScalaJsPlugin)
Have a look at sbt-play-scalajs. It is an additional sbt plugin that helps integration with Play / sbt-web.
Your build file will look something like this (copied from README):
import sbt.Project.projectToRef
lazy val jsProjects = Seq(js)
lazy val jvm = project.settings(
scalaJSProjects := jsProjects,
pipelineStages := Seq(scalaJSProd)).
enablePlugins(PlayScala).
aggregate(jsProjects.map(projectToRef): _*)
lazy val js = project.enablePlugins(ScalaJSPlugin, ScalaJSPlay)
By default, Scalatra expects the "webapp" directory to be at src/main/webapp. How could that be changed to, e.g., content/doc-root?
sbt allows for customizing its default directories using something like the following:
scalaSource <<= (baseDirectory)(_ / "src")
So I assume it's just a matter of knowing the right "configuration key" to use...
#Kelsey Gilmore-Innis has the right answer, but since it's not accepted let's break it, break it, break it down.
First, I'm assuming you're following Getting started guide to install Scalatra using g8. Hopefully the same version I just got.
g8 scalatra/scalatra-sbt
What that g8 template did was to set up an sbt 0.13 build which uses scalatra-sbt 0.3.2 plugin:
addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.3.2")
This plugin internally uses JamesEarlDouglas/xsbt-web-plugin 0.4.0 to do the webapp-related settings.
xsbt-web-plugin 0.4.0
This is why xsbt-web-plugin becomes relevant even though you just want to change Scalatra's setting. The setting you need to rewire is called webappResources in Compile. How does that work?
rewiring webappResources
To rewire the setting, open project/build.scala. Add
import com.earldouglas.xsbtwebplugin.PluginKeys.webappResources
to the import clauses. Then change settings as follows:
lazy val project = Project (
"foo",
file("."),
settings = Defaults.defaultSettings ++ ScalatraPlugin.scalatraWithJRebel ++ scalateSettings ++ Seq(
organization := Organization,
name := Name,
version := Version,
scalaVersion := ScalaVersion,
resolvers += Classpaths.typesafeReleases,
webappResources in Compile := Seq(baseDirectory.value / "content" / "doc-root"),
...
)
)
Now move src/main/webapp to content/doc-root, reload sbt, and that should be it.
The resource folder is a Jetty property. If you're running embedded Jetty, it's specified here. You can edit it manually or override by setting the PUBLIC environment variable.
You also can override it in your SBT build file. It uses the xsbt-web-plugin to run, and you can override that plugin's settings.
For newer version of xsbt-web-plugin (1.0.0 as of writing) the way of changing source path is different.
First of all corresponding settings were moved to XwpPlugin.webappSettings. And you needs these two
webappSrc in webapp <<= (baseDirectory in Compile) map { _ / "content" / "doc-root" },
webappDest in webapp <<= (baseDirectory in Compile) map { _ / "content" / "doc-root" },
If you dont want to change the sbt settings, you can also do it programmatically by overriding serveStaticResource and using forward
override protected def serveStaticResource(): Option[Any] = {
// check to see if we need to alter the path to find the TRUE disk url
val incUrl = request.getRequestURI
if(incUrl.startsWith("/otherDir")) {
servletContext.resource(request) map { _ =>
servletContext.getNamedDispatcher("default").forward(request, response)
}
} else {
val trueUrl = "/otherdir" + incUrl
Option(servletContext.getRequestDispatcher(trueUrl).forward(request, response))
}
}
Disclaimer: You should also check that it doesn't go into an infinite loop.