Skip to content

Commit 25baa0b

Browse files
LMnetdimafeng
authored andcommitted
Deprecate old lifecycle methods (#63)
1 parent ef3e2d7 commit 25baa0b

File tree

10 files changed

+264
-84
lines changed

10 files changed

+264
-84
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ class MysqlSpec extends FlatSpec with ForAllTestContainer {
256256

257257
## Release notes
258258

259+
* **0.27.0**
260+
* New `TestLifecycleAware` trait introduced. You can use it when you want to do something with the container before or after the test.
261+
* `Container` now implements `Startable` interface with `start` and `stop` methods.
262+
* Old container's lifecycle methods `finished`, `succeeded`, `starting`, `failed` are deprecated. Use `start`, `stop`, and `TestLifecycleAware` methods instead.
263+
259264
* **0.26.0**
260265
* TestContainers `1.11.2` -> `1.11.3`
261266
* Scala 2.13.0

src/main/scala/com/dimafeng/testcontainers/DockerComposeContainer.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,7 @@ class DockerComposeContainer (composeFiles: ComposeFile,
111111

112112
def getServicePort(serviceName: String, servicePort: Int): Int = container.getServicePort(serviceName, servicePort)
113113

114-
}
114+
override def start(): Unit = container.start()
115+
116+
override def stop(): Unit = container.stop()
117+
}

src/main/scala/com/dimafeng/testcontainers/MultipleContainers.scala

+42-2
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,36 @@
11
package com.dimafeng.testcontainers
22

3+
import com.dimafeng.testcontainers.lifecycle.TestLifecycleAware
34
import org.junit.runner.Description
5+
import org.testcontainers.lifecycle.TestDescription
46

57
import scala.language.implicitConversions
68

7-
class MultipleContainers private(containers: Seq[LazyContainer[_]]) extends Container {
9+
class MultipleContainers private(containers: Seq[LazyContainer[_]]) extends Container with TestLifecycleAware {
810

11+
@deprecated("Use `stop` instead")
912
override def finished()(implicit description: Description): Unit = containers.foreach(_.finished()(description))
1013

14+
@deprecated("Use `stop` and/or `TestLifecycleAware.afterTest` instead")
1115
override def succeeded()(implicit description: Description): Unit = containers.foreach(_.succeeded()(description))
1216

17+
@deprecated("Use `start` instead")
1318
override def starting()(implicit description: Description): Unit = containers.foreach(_.starting()(description))
1419

20+
@deprecated("Use `stop` and/or `TestLifecycleAware.afterTest` instead")
1521
override def failed(e: Throwable)(implicit description: Description): Unit = containers.foreach(_.failed(e)(description))
22+
23+
override def beforeTest(description: TestDescription): Unit = {
24+
containers.foreach(_.beforeTest(description))
25+
}
26+
27+
override def afterTest(description: TestDescription, throwable: Option[Throwable]): Unit = {
28+
containers.foreach(_.afterTest(description, throwable))
29+
}
30+
31+
override def start(): Unit = containers.foreach(_.start())
32+
33+
override def stop(): Unit = containers.foreach(_.stop())
1634
}
1735

1836
object MultipleContainers {
@@ -47,16 +65,38 @@ object MultipleContainers {
4765
* You don't need to wrap your containers into the `LazyContainer` manually
4866
* when you pass your containers in the `MultipleContainers`- there is implicit conversion for that.
4967
*/
50-
class LazyContainer[T <: Container](factory: => T) extends Container {
68+
class LazyContainer[T <: Container](factory: => T) extends Container with TestLifecycleAware {
5169
lazy val container: T = factory
5270

71+
@deprecated("Use `stop` instead")
5372
override def finished()(implicit description: Description): Unit = container.finished
5473

74+
@deprecated("Use `stop` and/or `TestLifecycleAware.afterTest` instead")
5575
override def failed(e: Throwable)(implicit description: Description): Unit = container.failed(e)
5676

77+
@deprecated("Use `start` instead")
5778
override def starting()(implicit description: Description): Unit = container.starting()
5879

80+
@deprecated("Use `stop` and/or `TestLifecycleAware.afterTest` instead")
5981
override def succeeded()(implicit description: Description): Unit = container.succeeded()
82+
83+
override def beforeTest(description: TestDescription): Unit = {
84+
container match {
85+
case c: TestLifecycleAware => c.beforeTest(description)
86+
case _ => // do nothing
87+
}
88+
}
89+
90+
override def afterTest(description: TestDescription, throwable: Option[Throwable]): Unit = {
91+
container match {
92+
case c: TestLifecycleAware => c.afterTest(description, throwable)
93+
case _ => // do nothing
94+
}
95+
}
96+
97+
override def start(): Unit = container.start()
98+
99+
override def stop(): Unit = container.stop()
60100
}
61101

62102
object LazyContainer {

src/main/scala/com/dimafeng/testcontainers/SeleniumTestContainerSuite.scala

+12-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package com.dimafeng.testcontainers
22

33
import java.io.File
44
import java.net.URL
5+
import java.util.Optional
56

7+
import com.dimafeng.testcontainers.lifecycle.TestLifecycleAware
68
import org.openqa.selenium.WebDriver
79
import org.openqa.selenium.remote.{DesiredCapabilities, RemoteWebDriver}
810
import org.scalatest.Suite
911
import org.testcontainers.containers.BrowserWebDriverContainer
12+
import org.testcontainers.lifecycle.TestDescription
1013

1114

1215
trait SeleniumTestContainerSuite extends ForEachTestContainer {
@@ -23,7 +26,7 @@ trait SeleniumTestContainerSuite extends ForEachTestContainer {
2326

2427
class SeleniumContainer(desiredCapabilities: Option[DesiredCapabilities] = None,
2528
recordingMode: Option[(BrowserWebDriverContainer.VncRecordingMode, File)] = None)
26-
extends SingleContainer[BrowserWebDriverContainer[_]] {
29+
extends SingleContainer[BrowserWebDriverContainer[_]] with TestLifecycleAware {
2730
require(desiredCapabilities.isDefined, "'desiredCapabilities' is required parameter")
2831

2932
type OTCContainer = BrowserWebDriverContainer[T] forSome {type T <: BrowserWebDriverContainer[T]}
@@ -40,6 +43,14 @@ class SeleniumContainer(desiredCapabilities: Option[DesiredCapabilities] = None,
4043
def vncAddress: String = container.getVncAddress
4144

4245
def webDriver: RemoteWebDriver = container.getWebDriver
46+
47+
override def afterTest(description: TestDescription, throwable: Option[Throwable]): Unit = {
48+
val javaThrowable: Optional[Throwable] = throwable match {
49+
case Some(error) => Optional.of(error)
50+
case None => Optional.empty()
51+
}
52+
container.afterTest(description, javaThrowable)
53+
}
4354
}
4455

4556
object SeleniumContainer {

src/main/scala/com/dimafeng/testcontainers/TestContainer.scala

+124-29
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package com.dimafeng.testcontainers
22

33
import java.util.function.Consumer
44

5+
import com.dimafeng.testcontainers.TestContainers.TestContainersSuite
6+
import com.dimafeng.testcontainers.lifecycle.TestLifecycleAware
7+
import org.junit.runner.{Description => JunitDescription}
58
import com.github.dockerjava.api.DockerClient
69
import com.github.dockerjava.api.command.{CreateContainerCmd, InspectContainerResponse}
710
import com.github.dockerjava.api.model.{Bind, Info, VolumesFrom}
@@ -11,60 +14,113 @@ import org.testcontainers.containers.output.OutputFrame
1114
import org.testcontainers.containers.startupcheck.StartupCheckStrategy
1215
import org.testcontainers.containers.traits.LinkableContainer
1316
import org.testcontainers.containers.{FailureDetectingExternalResource, Network, TestContainerAccessor, GenericContainer => OTCGenericContainer}
17+
import org.testcontainers.lifecycle.{Startable, TestDescription}
1418

1519
import scala.collection.JavaConverters._
1620
import scala.concurrent.{Future, blocking}
1721

18-
trait ForEachTestContainer extends SuiteMixin {
19-
self: Suite =>
22+
private[testcontainers] object TestContainers {
23+
24+
implicit def junit2testContainersDescription(junit: JunitDescription): TestDescription = {
25+
new TestDescription {
26+
override def getTestId: String = junit.getDisplayName
27+
override def getFilesystemFriendlyName: String = s"${junit.getClassName}-${junit.getMethodName}"
28+
}
29+
}
30+
31+
// Copy-pasted from `org.scalatest.junit.JUnitRunner.createDescription`
32+
def createDescription(suite: Suite): JunitDescription = {
33+
val description = JunitDescription.createSuiteDescription(suite.getClass)
34+
// If we don't add the testNames and nested suites in, we get
35+
// Unrooted Tests show up in Eclipse
36+
for (name <- suite.testNames) {
37+
description.addChild(JunitDescription.createTestDescription(suite.getClass, name))
38+
}
39+
for (nestedSuite <- suite.nestedSuites) {
40+
description.addChild(createDescription(nestedSuite))
41+
}
42+
description
43+
}
44+
45+
trait TestContainersSuite extends SuiteMixin { self: Suite =>
46+
47+
val container: Container
48+
49+
def afterStart(): Unit = {}
50+
51+
def beforeStop(): Unit = {}
52+
53+
private val suiteDescription = createDescription(self)
2054

21-
val container: Container
55+
private[testcontainers] def beforeTest(): Unit = {
56+
container match {
57+
case container: TestLifecycleAware => container.beforeTest(suiteDescription)
58+
case _ => // do nothing
59+
}
60+
}
2261

23-
implicit private val suiteDescription = Description.createSuiteDescription(self.getClass)
62+
private[testcontainers] def afterTest(throwable: Option[Throwable]): Unit = {
63+
container match {
64+
case container: TestLifecycleAware => container.afterTest(suiteDescription, throwable)
65+
case _ => // do nothing
66+
}
67+
}
68+
}
69+
}
70+
71+
trait ForEachTestContainer extends TestContainersSuite {
72+
self: Suite =>
2473

2574
abstract protected override def runTest(testName: String, args: Args): Status = {
26-
container.starting()
75+
container.start()
76+
77+
@volatile var testCalled = false
78+
@volatile var afterTestCalled = false
79+
2780
try {
2881
afterStart()
82+
beforeTest()
83+
84+
testCalled = true
2985
val status = super.runTest(testName, args)
30-
status match {
31-
case FailedStatus => container.failed(new RuntimeException(status.toString))
32-
case _ => container.succeeded()
86+
87+
afterTestCalled = true
88+
if (!status.succeeds()) {
89+
afterTest(Some(new RuntimeException("Test failed")))
90+
} else {
91+
afterTest(None)
3392
}
93+
3494
status
3595
}
3696
catch {
3797
case e: Throwable =>
38-
container.failed(e)
98+
if (testCalled && !afterTestCalled) {
99+
afterTestCalled = true
100+
afterTest(Some(e))
101+
}
102+
39103
throw e
40104
}
41105
finally {
42106
try {
43107
beforeStop()
44108
}
45109
finally {
46-
container.finished()
110+
container.stop()
47111
}
48112
}
49113
}
50-
51-
def afterStart(): Unit = {}
52-
53-
def beforeStop(): Unit = {}
54114
}
55115

56-
trait ForAllTestContainer extends SuiteMixin {
116+
trait ForAllTestContainer extends TestContainersSuite {
57117
self: Suite =>
58118

59-
val container: Container
60-
61-
implicit private val suiteDescription = Description.createSuiteDescription(self.getClass)
62-
63119
abstract override def run(testName: Option[String], args: Args): Status = {
64120
if (expectedTestCount(args.filter) == 0) {
65121
new CompositeStatus(Set.empty)
66122
} else {
67-
container.starting()
123+
container.start()
68124
try {
69125
afterStart()
70126
super.run(testName, args)
@@ -73,43 +129,82 @@ trait ForAllTestContainer extends SuiteMixin {
73129
beforeStop()
74130
}
75131
finally {
76-
container.finished()
132+
container.stop()
77133
}
78134
}
79135
}
80136
}
81137

82-
def afterStart(): Unit = {}
138+
abstract protected override def runTest(testName: String, args: Args): Status = {
139+
@volatile var testCalled = false
140+
@volatile var afterTestCalled = false
141+
142+
try {
143+
beforeTest()
144+
145+
testCalled = true
146+
val status = super.runTest(testName, args)
147+
148+
afterTestCalled = true
149+
if (!status.succeeds()) {
150+
afterTest(Some(new RuntimeException("Test failed")))
151+
} else {
152+
afterTest(None)
153+
}
83154

84-
def beforeStop(): Unit = {}
155+
status
156+
}
157+
catch {
158+
case e: Throwable =>
159+
if (testCalled && !afterTestCalled) {
160+
afterTestCalled = true
161+
afterTest(Some(e))
162+
}
163+
164+
throw e
165+
}
166+
}
85167
}
86168

87-
trait Container {
88-
def finished()(implicit description: Description): Unit
169+
trait Container extends Startable {
89170

90-
def failed(e: Throwable)(implicit description: Description): Unit
171+
@deprecated("Use `stop` instead")
172+
def finished()(implicit description: Description): Unit = stop()
91173

92-
def starting()(implicit description: Description): Unit
174+
@deprecated("Use `stop` and/or `TestLifecycleAware.afterTest` instead")
175+
def failed(e: Throwable)(implicit description: Description): Unit = {}
93176

94-
def succeeded()(implicit description: Description): Unit
177+
@deprecated("Use `start` instead")
178+
def starting()(implicit description: Description): Unit = start()
179+
180+
@deprecated("Use `stop` and/or `TestLifecycleAware.afterTest` instead")
181+
def succeeded()(implicit description: Description): Unit = {}
95182
}
96183

97184
trait TestContainerProxy[T <: FailureDetectingExternalResource] extends Container {
98185

99186
@deprecated("Please use reflective methods from the wrapper and `configure` method for creation")
100-
implicit val container: T
187+
implicit def container: T
101188

189+
@deprecated("Use `stop` instead")
102190
override def finished()(implicit description: Description): Unit = TestContainerAccessor.finished(description)
103191

192+
@deprecated("Use `stop` and/or `TestLifecycleAware.afterTest` instead")
104193
override def succeeded()(implicit description: Description): Unit = TestContainerAccessor.succeeded(description)
105194

195+
@deprecated("Use `start` instead")
106196
override def starting()(implicit description: Description): Unit = TestContainerAccessor.starting(description)
107197

198+
@deprecated("Use `stop` and/or `TestLifecycleAware.afterTest` instead")
108199
override def failed(e: Throwable)(implicit description: Description): Unit = TestContainerAccessor.failed(e, description)
109200
}
110201

111202
abstract class SingleContainer[T <: OTCGenericContainer[_]] extends TestContainerProxy[T] {
112203

204+
override def start(): Unit = container.start()
205+
206+
override def stop(): Unit = container.stop()
207+
113208
def binds: Seq[Bind] = container.getBinds.asScala.toSeq
114209

115210
def command: Seq[String] = container.getCommandParts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.dimafeng.testcontainers.lifecycle
2+
3+
import org.testcontainers.lifecycle.TestDescription
4+
5+
trait TestLifecycleAware {
6+
7+
def beforeTest(description: TestDescription): Unit = {}
8+
9+
def afterTest(description: TestDescription, throwable: Option[Throwable]): Unit = {}
10+
}

src/main/scala/org/testcontainers/containers/TestContainerAccessor.scala

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.testcontainers.containers
22

33
import org.junit.runner.Description
44

5+
@deprecated("Should be replaced by lifecycle methods")
56
object TestContainerAccessor {
67
def finished[T <:FailureDetectingExternalResource](description: Description)(implicit container: T): Unit =
78
container.finished(description)

0 commit comments

Comments
 (0)