Using JUnit #Rule with ScalaTest (e.g. TemporaryFolder) - scala

I would like to be able to use JUnit rules such as TemporaryFolder or other TestRules we have already developed in-house.
What is the best method to accomplish that? I'm aware of JUnitSuite but it doesn't seem to pick up the #Rule annotation.
I would like to use a different ScalaTest suite anyway.
So my questions are:
Are JUnit rules supported by a ScalaTest suit?
If not, is there a library out there which would make using Junit TestRules possible?
If not, how to use JUnit TestRules in Scala tests?
Or is there a more appropriate Scala-specific approach for acomplishing what TemporaryFolder, or, e.g., Stefan Birkner's System Rules provide?
Here's what I tried with JUnitSuite:
class MyTest extends JUnitSuite {
//#Rule
//val temporaryFolder = new TemporaryFolder() // throws java.lang.Exception: The #Rule 'temporaryFolder' must be public.
#Rule
def temporaryFolder = new TemporaryFolder()
#Test
def test: Unit = {
assert(temporaryFolder.newFile() !== null) // java.lang.IllegalStateException: the temporary folder has not yet been created
}
}

You could solve the problem by creating a member field of type TemporaryFolder and returning this field value by the #Rule function.
class MyTest extends JUnitSuite {
val _temporaryFolder = new TemporaryFolder
#Rule
def temporaryFolder = _temporaryFolder
#Test
def test: Unit = {
assert(temporaryFolder.newFile() !== null)
}
}

Here is what I came up based on ScalaTest's documentation on fixtures. Still, I would like to know if there is a better solution.
Loan-fixture method
class LoanFixtureTest extends FunSuite {
def withRule[T <: TestRule](rule: T)(testCode: T => Any): Unit = {
rule(
new Statement() {
override def evaluate(): Unit = testCode(rule)
},
Description.createSuiteDescription("JUnit rule wrapper")
).evaluate()
}
test("my test") {
withRule(new TemporaryFolder()) { temporaryFolder =>
assert(temporaryFolder.newFile() !== null)
}
}
}
Pros: allows applying the rule only to tests where it is needed
Cons: not very elegant usage; clumsy when multiple TestRules are required
Using stackable mixins with withFixture(test: NoArgTest) override
trait TemporaryFolderFixture1 extends SuiteMixin {
this: Suite =>
val temporaryFolder = new TemporaryFolder
abstract override def withFixture(test: NoArgTest) = {
var outcome: Outcome = null
val statementBody = () => outcome = super.withFixture(test)
temporaryFolder(
new Statement() {
override def evaluate(): Unit = statementBody()
},
Description.createSuiteDescription("JUnit rule wrapper")
).evaluate()
outcome
}
}
class StackableTraitFixtureTest extends FunSuite with TemporaryFolderFixture1 {
test("my test") {
assert(temporaryFolder.newFile() !== null)
}
}
Pros: very simple usage, conveniently allows mixing multiple rules in
Cons: requires having a mixin for every rule; rules need to be invoked even for tests that don't need them; rule cannot be used e.g. in BeforeAfterEach#beforeEach()
Overriding withFixture(test: OneArgTest)
trait TemporaryFolderFixture2 {
thisFixture: org.scalatest.fixture.FunSuite =>
type FixtureParam = TemporaryFolder
override protected def withFixture(test: OneArgTest): Outcome = {
val temporaryFolder = new TemporaryFolder()
var outcome: Outcome = null
temporaryFolder(
new Statement() {
override def evaluate(): Unit = {
outcome = withFixture(test.toNoArgTest(temporaryFolder))
}
},
Description.createSuiteDescription("JUnit rule wrapper")
).evaluate()
outcome
}
}
class OneArgWithFixtureTest extends org.scalatest.fixture.FunSuite with TemporaryFolderFixture2 {
test("my test") { temporaryFolder =>
assert(temporaryFolder.newFile() !== null)
}
}
Cons: allows only one TestRule, making in generic to work with any rule instead of just TestRule would require an extra effort
Which one do you like the best?

This worked for me. Based on answer. So annotation will be applied to
to the (synthetic) getter method
import org.junit._
import scala.annotation.meta.getter
class MyTest extends JUnitSuite {
#(Rule #getter)
val tempFolder = new TemporaryFolder
}
Just make sure to use junit version >4.11.

Related

Is there any way to rewrite the below code using Scala value class or other concept?

I need to write two functions to get the output format and the output index for file conversion. As part of this, I wrote a TransformSettings class for these methods and set the default value. And in the transformer class, I created a new object of TransformSettings class to get the default values for each job run. Also, I have another class called ParquetTransformer that extends Transformer where I want to change these default values. So I implemented like below.
class TransformSettings{
def getOuputFormat: String = {
"orc"
}
def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("orc.column.index.access")
}
}
class Transformer{
def getTransformSettings: TransformSettings = {
new TransformSettings
}
def posttransform(table: AWSGlueDDL.Table):Dateframe ={
val indexAccess = getTransformSettings.getOuputIndex(table: AWSGlueDDL.Table)
........
}
}
class ParquetTransformer extends Transformer{
override def getTransformSettings: TransformSettings = {
val transformSettings = new TransformSettings {
override def getOuputFormat: String = {
"parquet"
}
override def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("parquet.column.index.access")
}
}
}
}
Is there a way to avoid creating a brand new object of TransformSettings in Transfomer class every time this is called?
Also is there a way to rewrite the code using Scala value class?
As #Dima proposed in the comments try to make TransformSettings a field / constructor parameter (a val) in the class Transformer and instantiate them outside
class TransformSettings{
def getOuputFormat: String = {
"orc"
}
def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("orc.column.index.access")
}
}
class Transformer(val transformSettings: TransformSettings) {
def posttransform(table: AWSGlueDDL.Table): DataFrame ={
val indexAccess = transformSettings.getOuputIndex(table: AWSGlueDDL.Table)
???
}
}
val parquetTransformSettings = new TransformSettings {
override def getOuputFormat: String = {
"parquet"
}
override def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("parquet.column.index.access")
}
}
class ParquetTransformer extends Transformer(parquetTransformSettings)
You don't seem to need value classes (... extends AnyVal) now. They are more about unboxing, not about life-cycle management. TransformSettings and Transformer can't be value classes because they are not final (you're extending them in class ParquetTransformer extends Transformer... and new TransformSettings { ... }). By the way, value classes have many limatations
https://failex.blogspot.com/2017/04/the-high-cost-of-anyval-subclasses.html
https://github.com/scala/bug/issues/12271
Besides value classes, there are scala-newtype library in Scala 2 and opaque types in Scala 3.

Why does it matter what order I mix in TestSuiteMixIn traits in scalatest suites?

I created the following fixtures:
trait DatabaseFixture extends TestSuiteMixin { this: TestSuite =>
// Just setting up a test database
val cpds = new ComboPooledDataSource
val url : URL = getClass.getResource( "c3p0.properties" )
val db = Database.forDataSource(cpds, Some(50))
val users = Schema.users
val instances = Schema.instances
Await.result(db.run( DBIO.seq(
users.schema.create,
) ), 3 seconds )
abstract override def withFixture(test: NoArgTest): Outcome = {
try super.withFixture(test)
finally cpds.close()
}
}
trait UserControllerFixture extends DatabaseFixture with ScalatraSuite { this: TestSuite =>
addServlet( new UserController(db), "/user/*" )
abstract override def withFixture(test: NoArgTest): Outcome = {
super.withFixture( test )
}
}
Here is the first way I mixed them in to a test suite:
class UserControllerTestSuite extends DatabaseFixture with ScalatraSuite with FlatSpecLike with Matchers {
"POST to /user/add" should "return 201 for created" in {
post( "/instance/add" ) {
status shouldBe 201
}
}
}
This failed to compile with the following error: method withFixture in trait TestSuite of type (test: UserControllerTestSuite.this.NoArgTest)org.scalatest.Outcome has weaker access privileges; it should be public
However, when I mixed the fixtures in after the other scalatest traits, it compiled fine:
class UserControllerTestSuite extends ScalatraSuite with FlatSpecLike with Matchers with DatabaseFixture {
"POST to /user/add" should "return 201 for created" in {
post( "/instance/add" ) {
status shouldBe 201
}
}
}
What's going on here? What does it mean that withFixture() has "weaker access privileges"?
Mixins in Scala are scanned right to left. This is why DatabaseFixture is called before other traits in case where your code works.
So before when there was some other trait (TestSuite) before DatabaseFixture with withFixture method , it tried to override it "weaker access privilege", which means exactly what it says. You cannot override public method with private for example. It has to be the same priority or higher (public > protected in your case.)

Integration tests for Http4s Client/Resource

I'm implementing a Vault client in Scala using Http4s client.
And I'm now starting to write integration tests. So far I have this:
abstract class Utils extends AsyncWordSpec with Matchers {
implicit override def executionContext = ExecutionContext.global
implicit val timer: Timer[IO] = IO.timer(executionContext)
implicit val cs: ContextShift[IO] = IO.contextShift(executionContext)
val vaultUri = Uri.unsafeFromString(Properties.envOrElse("VAULT_ADDR", throw IllegalArgumentException))
val vaultToken = Properties.envOrElse("VAULT_TOKEN", throw IllegalArgumentException)
val clientResource = BlazeClientBuilder[IO](global)
.withCheckEndpointAuthentication(false)
.resource
def usingClient[T](f: VaultClient[IO] => IO[Assertion]): Future[Assertion] = {
clientResource.use { implicit client =>
f(new VaultClient[IO](vaultUri, vaultToken))
}.unsafeToFuture()
}
}
Then my tests look like this (just showing one test):
class SysSpec extends Utils {
"The auth endpoint" should {
"successfully mount an authentication method" in {
usingClient { client =>
for {
result <- client.sys.auth.create("test", AuthMethod(
"approle", "some nice description", config = TuneOptions(defaultLeaseTtl = 60.minutes)
))
} yield result should be (())
}
}
}
}
This approach works, however it doesn't feel right. For each test I'm opening the connection (clientResource.use) and recreating the VaultClient.
Is there a way for me to reuse the same connection and client for all the tests in SysSpec.
Please note these are integration tests and not unit tests.
This is the best I could come up with.
abstract class Utils extends AsyncWordSpec with Matchers with BeforeAndAfterAll {
implicit override def executionContext = ExecutionContext.global
implicit val timer: Timer[IO] = IO.timer(executionContext)
implicit val cs: ContextShift[IO] = IO.contextShift(executionContext)
val (httpClient, finalizer) = BlazeClientBuilder[IO](global)
.withCheckEndpointAuthentication(false)
.resource.allocated.unsafeRunSync()
override protected def afterAll(): Unit = finalizer.unsafeRunSync()
private implicit val c = httpClient
val client = new VaultClient[IO](uri"http://[::1]:8200", "the root token fetched from somewhere")
}
Then the tests just use the client directly:
class SysSpec extends Utils {
"The auth endpoint" should {
"successfully mount an authentication method" in {
client.sys.auth.create("test", AuthMethod(
"approle", "some nice description",
config = TuneOptions(defaultLeaseTtl = 60.minutes))
).map(_ shouldBe ()).unsafeToFuture()
}
}
}
My two main problems with this approach are the two unsafeRunSyncs in the code. The first one is to create the client and the second one to clean the resource. However it is a much better approach then repeatedly creating and destroy the client.
I would also like not to use the unsafeToFuture but that would require ScalaTest to support Cats-Effect directly.

How to inject Configuration instance to scalatest?

I want to inject Configuration instance in one of my testing classes, I extend my test class with ConfiguredApp and injected the Configuration, it looks like this:
#DoNotDiscover()
class MyApiServiceSpec extends FreeSpec with ScalaFutures with ConfiguredApp {
implicit val formats = DefaultFormats
implicit val exec = global
lazy val configuration = app.injector.instanceOf[Configuration]
"Global test" - {
"testcase 1" in {
Server.withRouter() {
case GET(p"/get/data") => Action { request =>
Results.Ok()
}
} { implicit port =>
WsTestClient.withClient { implicit client =>
val service = new MyApiService {
override def config: Configuration = configuration
override val ws: WSClient = client
}
whenReady(service.getData()) { res =>
//i will test stuff here
}
}
}
}
}
}
(MyApiService is a trait)
Exception encountered when invoking run on a nested suite -
ConfiguredApp needs an Application value associated with key
"org.scalatestplus.play.app" in the config map. Did you forget to
annotate a nested suite with #DoNotDiscover?
java.lang.IllegalArgumentException: ConfiguredApp needs an Application
value associated with key "org.scalatestplus.play.app" in the config
map. Did you forget to annotate a nested suite with #DoNotDiscover?
someone have an idea why is that...?
thanks!333333
My answer is not answer to current question, but I want give some advice. If you want to write unit tests for controllers or some service, I would suggest to use a PlaySpec. In order to inject custom configuration for testing environment:
class MyControllerSpec extends PlaySpec with OneAppPerSuite {
val myConfigFile = new File("app/test/conf/application_test.conf")
val parsedConfig = ConfigFactory.parseFile(myConfigFile)
val configuration = ConfigFactory.load(parsedConfig)
implicit override lazy val app: Application = new GuiceApplicationBuilder()
.overrides(bind[Configuration].toInstance(Configuration(configuration)))
.build()
"MyController #index" should {
"should be open" in {
val result = route(app, FakeRequest(GET, controllers.routes.MyController.index().url)).get
status(result) mustBe OK
}
}
}
It seems that you tried to run this test alone. But with a ConfiguredAppyou must run this test with a Suite, like
class AcceptanceSpecSuite extends PlaySpec with GuiceOneAppPerSuite {
override def nestedSuites = Vector(new MyApiServiceSpec)
}
The injection looks ok.

Specs2 with Scaldi - wrong implicit injector being invoked

I'm trying to run a test with scaldi and specs2. In the test I need to override a StringManipulator function that uses an injected ProxyManipulator. The ProxyManipulator takes a string and returns its upper case in a Future. The replacement manipulator in the test returns a Future("Test Message").
Here is the StringManipulator class where the injection occurs:
class StringManipulator {
def manip (str : String) (implicit inj: Injector) : String = {
val prox = inject[ProxyManipulator]
Await.result(prox.manipulate(str), 1 second)
}
}
I'm using a package.object that contains the implicit injector:
import modules.MyModule
package object controllers {
implicit val appModule = new MyModule
}
And here is the specs2 test with the new binding:
#RunWith(classOf[JUnitRunner])
class StringManipScaldiSpec extends Specification {
class TestModule extends Module {
bind [ProxyManipulator] to new ProxyManipulator {
override def manipulate(name: String) = Future("Test Message")
}
}
"Application" should {
"do something" in {
val myTestModule = new TestModule
val str = "my string"
val stringMan = new StringManipulator() //(myTestModule)
stringMan.manip(str)(myTestModule) === "Test Message"
}
}
}
The problem is that when the test runs the class StringManipulator is still using the original Proxy Manipulator instead of the one passed in the TestModule. Any ideas?