SkyWalker
SkyWalker

Reputation: 14317

ScalaTest: Is there a flaw defining Test Suites as object rather than class?

I have a very specific use-case and in order to reduce instantiation time for the test suites I define them as an object and not as a class e.g.

import org.scalatest._
import scala.collection.mutable.Stack

object StackSuite extends FunSuite with Matchers {
    test("stackShouldPopValuesIinLastInFirstOutOrder") {
        val stack = new Stack[Int]
        stack.push(1)
        stack.push(2)
        stack.pop() should equal(2)
        stack.pop() should equal(1)
     }
}

This works but is there a fundamental issue or flaw regarding doing this? separate from the multithreading issue should each test be executed separately and in the unfortunate case of having modifyable state at the suite level.

UPDATE: when I say "reduce instantiation time" I meant overall e.g. accounting for heavyweight fixture data inside the object/class and not simply the pure Suite class instantiation time. Since object is a singleton then it would be a lookup rather than an instantiation of a new class.

I need to do this in order to distribute millions of tests across a grid of computers. Some tests are standard scalatest others are evaluating the permutations of a set of input parameters therefore the need to reduce the startup time for the instantiation of the Suite.

btw I do this to execute each test in a cluster machine:

import scala.reflect.runtime.universe

// I receive the suite and test names as input   
val suite = "com.mycomp.StackSuite"
val test = "stackShouldPopValuesIinLastInFirstOutOrder"

val runtimeMirror = universe.runtimeMirror(getClass.getClassLoader)
val module = runtimeMirror.staticModule(suite)
val obj = runtimeMirror.reflectModule(module)
val status = obj.asInstanceOf[TestSuite].execute(test)

Upvotes: 1

Views: 340

Answers (2)

Alexey Romanov
Alexey Romanov

Reputation: 170815

in order to reduce instantiation time for the test suites I define them as an object and not as a class

I had a quick look at org.scalatest.tools.Runner (and DiscoverySuite) implementation, and I don't think this can reduce the instantiation time.

Remember how objects are actually compiled on JVM: you have a class StackSuite$ which extends Suite and has a public parameterless constructor (because it has to be called from StackSuite's static initializer). It's found and handled precisely like a class StackSuite would be.

EDIT: if you find the suite and run it yourself in this way instead of using ScalaTest's own API, then yes, you'll get a lookup instead of instantiation. Assuming no concurrency issues, there should be no problem: execute itself is threadsafe.

Upvotes: 2

Samar
Samar

Reputation: 2101

If you do not feel the need to modularize your testing by using inheritance then there is no problem in using an Object instead of a class. Else classes can help you in segregating the different kinds of testing.

From the ScalaTest docs :

Instead of duplicating code by mixing the same traits together repeatedly, we recommend you create abstract base classes for your project that mix together the features you use the most. For example, you might create a UnitSpec class (not trait, for speedier compiles) for unit tests that looks like:

package com.mycompany.myproject

import org.scalatest._

abstract class UnitSpec extends FlatSpec with Matchers with
OptionValues with Inside with Inspectors

You can then write unit tests for your project using the custom base class, like this:

package com.mycompany.myproject

import org.scalatest._

class MySpec extends UnitSpec {
  // Your tests here
}

Upvotes: 1

Related Questions