JUnit test runtime for Scala.js - provides the runtime infrastructure for running JUnit tests compiled with Scala.js
—
JUnit provides annotations to control test execution flow, allowing setup and teardown operations at both method and class levels. These annotations ensure tests run in isolation with proper initialization and cleanup.
class Test(
expected: Class[_ <: Throwable] = classOf[Test.None],
timeout: Long = 0L
) extends StaticAnnotation
object Test {
final class None private () extends Throwable
}Basic Usage:
class UserServiceTest {
@Test
def shouldCreateNewUser(): Unit = {
val user = userService.createUser("Alice", "alice@example.com")
assertNotNull(user)
assertEquals("Alice", user.getName())
}
}Expected Exception Testing:
@Test(expected = classOf[IllegalArgumentException])
def shouldRejectInvalidEmail(): Unit = {
userService.createUser("Bob", "invalid-email")
}Timeout Testing:
@Test(timeout = 1000L) // 1 second timeout
def shouldCompleteQuickly(): Unit = {
val result = quickOperation()
assertNotNull(result)
}Combined Parameters:
@Test(expected = classOf[TimeoutException], timeout = 5000L)
def shouldTimeoutWithException(): Unit = {
slowOperationThatTimesOut()
}class Before extends StaticAnnotationUsage:
class DatabaseTest {
private var database: Database = _
@Before
def setUp(): Unit = {
database = new TestDatabase()
database.connect()
database.initializeSchema()
}
@Test
def shouldInsertUser(): Unit = {
val user = User("Alice", "alice@example.com")
database.insert(user)
assertEquals(1, database.count())
}
@Test
def shouldUpdateUser(): Unit = {
val user = User("Bob", "bob@example.com")
database.insert(user)
database.update(user.copy(email = "bob.smith@example.com"))
assertEquals("bob.smith@example.com", database.findById(user.id).email)
}
}class After extends StaticAnnotationUsage:
class FileSystemTest {
private var tempDir: Path = _
@Before
def createTempDirectory(): Unit = {
tempDir = Files.createTempDirectory("test")
}
@After
def cleanupTempDirectory(): Unit = {
if (tempDir != null) {
Files.walk(tempDir)
.sorted(java.util.Comparator.reverseOrder())
.forEach(Files.delete)
}
}
@Test
def shouldCreateFile(): Unit = {
val file = tempDir.resolve("test.txt")
Files.write(file, "content".getBytes)
assertTrue(Files.exists(file))
}
}class BeforeClass extends StaticAnnotationUsage:
class IntegrationTest {
companion object {
private var server: TestServer = _
@BeforeClass
def startServer(): Unit = {
server = new TestServer(8080)
server.start()
// Wait for server to be ready
Thread.sleep(1000)
}
@AfterClass
def stopServer(): Unit = {
if (server != null) {
server.stop()
}
}
}
@Test
def shouldRespondToHealthCheck(): Unit = {
val response = httpClient.get("http://localhost:8080/health")
assertEquals(200, response.getStatusCode())
}
}class AfterClass extends StaticAnnotationMethods annotated with @AfterClass are executed once after all test methods in the class have run.
class Ignore(value: String = "") extends StaticAnnotationUsage:
class FeatureTest {
@Test
def shouldWork(): Unit = {
// This test runs normally
assertTrue(feature.isEnabled())
}
@Ignore("Feature not implemented yet")
@Test
def shouldHandleAdvancedCase(): Unit = {
// This test is skipped
feature.advancedOperation()
}
@Ignore // No reason provided
@Test
def temporarilyDisabled(): Unit = {
// This test is also skipped
flakeyOperation()
}
}Ignoring Entire Test Classes:
@Ignore("Integration tests disabled in CI")
class SlowIntegrationTest {
@Test
def shouldConnectToExternalService(): Unit = {
// All tests in this class are skipped
}
}trait Rule extends AnnotationUsage:
class RuleTest {
@Rule
val tempFolder: TemporaryFolder = new TemporaryFolder()
@Rule
val timeout: Timeout = new Timeout(10000) // 10 second timeout for all tests
@Test
def shouldUseTemporaryFolder(): Unit = {
val file = tempFolder.newFile("test.txt")
Files.write(file.toPath, "content".getBytes)
assertTrue(file.exists())
}
}trait ClassRule extends AnnotationUsage:
class ClassRuleTest {
companion object {
@ClassRule
val externalResource: ExternalResource = new ExternalResource() {
override def before(): Unit = {
// Setup expensive resource once for all tests
println("Setting up external resource")
}
override def after(): Unit = {
// Cleanup expensive resource after all tests
println("Cleaning up external resource")
}
}
}
@Test
def shouldUseExternalResource(): Unit = {
// Use the external resource
assertTrue(externalResource.isAvailable())
}
}JUnit executes lifecycle methods in the following order:
Exception Handling:
Resource Management: Always pair setup with cleanup (@Before with @After, @BeforeClass with @AfterClass)
Exception Safety: Use try-finally or similar patterns in @After methods to ensure cleanup
Test Isolation: Each test should be independent - @Before and @After ensure clean state
Expensive Resources: Use @BeforeClass/@AfterClass for expensive setup (database connections, web servers)
Temporary Resources: Use @Rule with TemporaryFolder, TestName, or custom rules for common patterns
Install with Tessl CLI
npx tessl i tessl/maven-org-scala-js--scalajs-junit-test-runtime