I would like to provide a test suite for an interface in our library so that clients that implement the interface can easily test their implementations for conformance with the specification of the interface.
A very simple example of this could look like this:
// Library code.
abstract class LibTest(impl: LibInterface) {
@Test
def myTest: Unit = {
assertTrue(impl.getTrue())
}
}
// Client code
class MyTest extends LibTest(new MyInterfaceImpl)
However, we need a couple of things more that turn out to be difficult with this approach. Notably:
- Parametrize some of our test cases internally (should not be visible to the client).
- Allow the client to pass properties of their implementation. Notably:
- The set of supported features (based on which we want to enable/disable tests).
- Supporting properties that tell the test how to interact with the implementation.
In our current prototype, we solve this by having suites per feature and constructors for parameters. Looks like this:
// Library code.
abstract class LibFeatureATest(impl: LibInterface, prop: String, paramA: Boolean)
abstract class LibFeatureBTest(impl: LibInterface, prop: String)
abstract class LibFeatureABTest(impl: LibInterface, prop: String, paramA: Boolean)
// Client code:
class FeatureATest(new MyInterfaceImpl, "my_prop", true)
class FeatureATest(new MyInterfaceImpl, "my_prop", false)
class FeatureBTest(new MyInterfaceImpl, "my_prop")
class FeatureABTest(new MyInterfaceImpl, "my_prop", true)
class FeatureABTest(new MyInterfaceImpl, "my_prop", false)
There are two main problems with this approach:
- The parameters for the tests need to be in the client.
- The client needs to instantiate tests for feature interactions.
Ideally we would like to have something like this in the client:
class MyTest extends TestSuite(new MyInterfaceImpl,
prop = "my_prop",
supportsA = true,
supportsB = true)
The exact invocation syntax / type are secondary (abstract methods, parameters, annotations). Note however these additional requirements:
- Needs to be JUnit, we cannot depend on Scala specific testing frameworks.
- Needs to work in sbt, so some experimental runners (e.g.
Enclosing
) might not work. - We would like to avoid unnecessary implementation inheritance.
How can we do this in JUnit4 in Scala on sbt?
You could implement JUnit Rules that provide parameterization and configuration, respectively.
The configuration (ie the properties given by the client) could be read from a file so the client can provide it separately without changing the test itself; the Rule would then enable or disable test cases depending on the config.
It's not clear what you mean by 'parameterized', but I'm sure you can use a Rule for that. They're essentially "Around" - aspects for test cases.
EDIT:
So this is how I imagine your interface:
A complete Unit Test for this would be along the lines of
But IIUC you want the client to be able to specify which features are supported, and if they aren't, disable that test case. That's why I annotated the test cases with the FeatureSupport annotation, which would be a
with a
So that's the setup to write the Rule, which would be something like