Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/modules/webdriver_containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ If you would like to customise the file name of the recording, or provide a diff

Note the factory must implement `org.testcontainers.containers.RecordingFileFactory`.

If you reuse a single `BrowserWebDriverContainer` across multiple tests (e.g. to avoid the cost of starting a new
browser container per test), call `restartVncRecording()` before each test so that `afterTest()` saves a separate
recording per test instead of one continuous recording for the whole container's lifetime:

<!--codeinclude-->
[Restart recording between tests](../../modules/selenium/src/test/java/org/testcontainers/selenium/ChromeRecordingWebDriverContainerTest.java) inside_block:restart
<!--/codeinclude-->

## More examples

A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/main/modules/selenium/src/test/java/org/testcontainers/selenium/ChromeWebDriverContainerTest.java).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,52 @@ public void afterTest(TestDescription description, Optional<Throwable> throwable
retainRecordingIfNeeded(description.getFilesystemFriendlyName(), !throwable.isPresent());
}

/**
* Restarts VNC recording, so that a separate recording is captured for each test method when a single
* {@link BrowserWebDriverContainer} instance is reused across multiple tests (e.g. to avoid the cost of
* starting a new browser container per test). Call this before each test starts; {@link #afterTest} will then
* save the recording captured since the last restart.
* <p>
* Does nothing if recording is not enabled ({@link VncRecordingMode#SKIP}) or the container has not started yet.
*
* @throws ContainerLaunchException if the replacement recording container fails to start. The previous
* recording container is stopped regardless, so recording is disabled (as if {@link VncRecordingMode#SKIP} had
* been used) rather than left in a stale or partially-started state.
*/
public void restartVncRecording() {
if (recordingMode == VncRecordingMode.SKIP || vncRecordingContainer == null) {
return;
}

VncRecordingContainer previousRecordingContainer = vncRecordingContainer;
// Clear the field before starting the replacement below: if start() throws, a stale reference to this
// now-stopped container must not be left in place for afterTest() to save from.
vncRecordingContainer = null;
try {
previousRecordingContainer.stop();
} catch (Exception e) {
LOGGER.debug("Failed to stop vncRecordingContainer", e);
}

VncRecordingContainer nextRecordingContainer = new VncRecordingContainer(this)
.withVncPassword(DEFAULT_PASSWORD)
.withVncPort(VNC_PORT)
.withVideoFormat(recordingFormat);
try {
nextRecordingContainer.start();
} catch (Exception e) {
// start() may have already created the underlying container (e.g. its wait strategy timed out) -
// stop it explicitly so it isn't left running until Ryuk reaps it.
try {
nextRecordingContainer.stop();
} catch (Exception stopException) {
e.addSuppressed(stopException);
}
throw new ContainerLaunchException("Failed to restart VNC recording container", e);
}
vncRecordingContainer = nextRecordingContainer;
}

@Override
public void stop() {
if (vncRecordingContainer != null) {
Expand Down Expand Up @@ -230,6 +276,12 @@ private void retainRecordingIfNeeded(String prefix, boolean succeeded) {
}

if (shouldRecord) {
if (vncRecordingContainer == null) {
// Can happen if restartVncRecording() failed to start a replacement recording container.
LOGGER.warn("No VNC recording container available for test {} - recording will not be saved", prefix);
return;
}

File recordingFile = recordingFileFactory.recordingFileForTest(
vncRecordingDirectory,
prefix,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,62 @@ private File[] runSimpleExploreInContainer(BrowserWebDriverContainer container,
TimeUnit.MILLISECONDS.sleep(MINIMUM_VIDEO_DURATION_MILLISECONDS);
doSimpleExplore(container, new ChromeOptions());
container.afterTest(
new TestDescription() {
@Override
public String getTestId() {
return getFilesystemFriendlyName();
}

@Override
public String getFilesystemFriendlyName() {
return "ChromeThatRecordsAllTests-recordingTestThatShouldBeRecordedAndRetained";
}
},
testDescription("ChromeThatRecordsAllTests-recordingTestThatShouldBeRecordedAndRetained"),
Optional.empty()
);

return vncRecordingDirectory.toFile().listFiles(new PatternFilenameFilter(fileNamePattern));
}

private TestDescription testDescription(String filesystemFriendlyName) {
return new TestDescription() {
@Override
public String getTestId() {
return getFilesystemFriendlyName();
}

@Override
public String getFilesystemFriendlyName() {
return filesystemFriendlyName;
}
};
}

@Test
void restartVncRecordingProducesASeparateFileForEachTest() throws InterruptedException {
File target = vncRecordingDirectory.toFile();
try (
// restart {
BrowserWebDriverContainer chrome = new BrowserWebDriverContainer("selenium/standalone-chrome:4.13.0")
.withRecordingMode(VncRecordingMode.RECORD_ALL, target)
.withRecordingFileFactory(new DefaultRecordingFileFactory())
.withNetwork(NETWORK)
) {
chrome.start();

TimeUnit.MILLISECONDS.sleep(MINIMUM_VIDEO_DURATION_MILLISECONDS);
doSimpleExplore(chrome, new ChromeOptions());
chrome.afterTest(
testDescription("restartVncRecordingProducesASeparateFileForEachTest-first"),
Optional.empty()
);

// Call this before each subsequent test so its recording doesn't get appended to the previous one
chrome.restartVncRecording();
// }

TimeUnit.MILLISECONDS.sleep(MINIMUM_VIDEO_DURATION_MILLISECONDS);
doSimpleExplore(chrome, new ChromeOptions());
chrome.afterTest(
testDescription("restartVncRecordingProducesASeparateFileForEachTest-second"),
Optional.empty()
);

File[] files = vncRecordingDirectory.toFile().listFiles(new PatternFilenameFilter("PASSED-.*\\.flv"));
assertThat(files).as("a separate recording file exists per test").hasSize(2);
}
}

@Test
void recordingTestShouldHaveFlvExtension() throws InterruptedException {
File target = vncRecordingDirectory.toFile();
Expand Down