SlideShare a Scribd company logo
Scott Leberknight
3/4/2021
JUnit Pioneer
“JUnit Pioneer is an extension pack for JUnit 5 or,
to be more precise, for the Jupiter engine”
Recap: JUnit Extensions
Provide a way to extend JUnit’s core features
Well-defined extension API and lifecycle
Simple Example
@ParameterizedTest
@RandomIntSource(min = 5, max = 10, count = 50)
void shouldDoSomethingWithRandomInts(int value) {
// test code...
}
Injecting method
parameters into
tests
Example from https://github.com/kiwiproject/kiwi-test/
@RegisterExtension
static final CuratorTestingServerExtension ZK_TEST_SERVER =
new CuratorTestingServerExtension();
Another Example
Start and stop a
ZooKeeper server before
& after tests
Example from https://github.com/kiwiproject/kiwi-test/
Pioneer Extensions
A collection of useful Jupiter extensions
Easy to integrate into your tests
Pioneer Extensions
Cartesian product
Locale & TimeZone
Environment Vars
Range sources
Issue Info
Disabling tests
Report entries
Stdin & Stdout
System props
Vintage tests
Stopwatch!
* as of 2021-03-04
*
System Properties
@SetSystemProperty(key = "user", value = "bob")
@SetSystemProperty(key = "id", value = "42")
class SystemPropertyTest {
@Test
@ClearSystemProperty(key = "user")
void testWithoutUserProperty() { ... }
@Test
@SetSystemProperty(key = "pwd", value = "12345")
void anotherTest() { ... }
}
Environment Variables
@SetEnvironmentVariable(key = "PWD", value="/tmp")
@ClearEnvironmentVariable(key = "PS1")
class EnvVarsTest {
@ClearEnvironmentVariable(key = "PWD")
void testWithoutWorkingDirEnvVar() { ... }
@SetEnvironmentVariable(key = "USER", value = "alice")
void testWithCustomUser() { ... }
}
Default TimeZone
@DefaultTimeZone("America/Mexico_City")
class DefaultTimeZoneTest {
@Test
void testInMexicoCity() {
assertThat(TimeZone.getDefault()).isEqualTo(
TimeZone.getTimeZone("America/Mexico_City"));
}
@Test
@DefaultTimeZone("Mexico/BajaSur")
void testInBajaSur() { ... }
}
Default Locale
@DefaultLocale("es")
class DefaultLocaleTest {
@Test
void testWithDefaultFromClass() {
assertThat(Locale.getDefault())
.isEqualTo(new Locale("es"));
}
@Test
@DefaultLocale(language = "nan", country = "TW",
variant = "Hant")
void testUsingAllAttributes() {
assertThat(Locale.getDefault()).isEqualTo(
new Locale("nan", "TW", "Hant"));
}
}
Having Issues?
public class SystemOutIssueProcessor
implements IssueProcessor {
@Override
public void processTestResults(List<IssueTestSuite> suites) {
// do something with each test suite
}
}
@Test
@Issue("JUPI-1234")
void shouldFix1234() {
// test code for issue JUPI-1234
}
#1 Annotate
#2 Define processor
#3 Register implementation
com.fortitudetec.junit.pioneering.SystemOutIssueProcessor
META-INF/services/org.junitpioneer.jupiter.IssueProcessor
I need those TPS reports...
class ReportEntriesTest {
@Test
@ReportEntry("I'm Going To Need Those TPS Reports ASAP")
void testWithReportEntry() { ... }
@Test
@ReportEntry(key = "result", value = "success",
when = PublishCondition.ON_SUCCESS)
@ReportEntry(key = "result", value = "failed",
when = ReportEntry.PublishCondition.ON_FAILURE)
void testWithConditionalEntries() { ... }
}
#1 Annotate
public class SimpleTestExecutionListener
implements TestExecutionListener { ... }
#2 Create a JUnit
TestExecutionListener
com.fortitudetec.junit.pioneering.SimpleTestExecutionListener
META-INF/services/org.junit.platform.launcher.TestExecutionListener
#3 Register implementation
Disabling on display name
Selectively disable parameterized tests
Specify substrings and/or regex to match...
...and disable any tests with a matching name
@DisableIfDisplayName(contains = "42")
@ParameterizedTest(name = "Test scenario {0}")
@ValueSource(ints = {1, 24, 42, 84, 168, 420, 4200, 4242, 17640})
void shouldDisableUsingStringContains(int value) {
if (String.valueOf(value).contains("42")) {
fail("Should not have received %s", value);
}
}
Using a substring to specify
disabling condition
matching
against the
generated
test name
@DisableIfDisplayName(matches = ".*d{3}-d{2}-d{4}.*")
@ParameterizedTest(name = "Test scenario {0}")
@ValueSource(strings = {
"42", "123-45-6789", "400", "234-56-7890", "888"
})
void shouldDisableUsingRegex(String value) {
failIfContainsAny(value, "123-45-6789", "234-56-7890");
}
Or use a regular
expression
@DisableIfDisplayName(contains = {"42", "84"},
matches = ".*d{3}-d{2}-d{4}.*")
@ParameterizedTest(name = "Test scenario {0}")
@ValueSource(strings = {
"24", "42", "123-45-6789", "400", "234-56-7890", "888"
})
void shouldDisableUsingContainsAndMatches(String value) {
failIfContainsAny(value, "42", "84", "123-45-6789", "234-56-7890");
}
or use both...
@DisableIfDisplayName(matches = ".*[0-9][3|5]")
@ParameterizedTest(name = "Product: FTCH-000-{0}")
@RandomIntSource(min = 0, max = 1_000, count = 500)
void shouldDisableWhenProductCodeEndsWith_X3_Or_X5(int code) {
var codeString = String.valueOf(code);
if (PRODUCT_NUMBER_PATTERN.matcher(codeString).matches()) {
fail("Should not have received %d", code);
}
}
Combine a regex
with randomized test input
@RandomIntSource from https://github.com/kiwiproject/kiwi-test/
Do, or do not, there is no try
class RetryingAnnotationTest {
@RetryingTest(2)
void shouldFail() {
flakyObject.failsFirstTwoTimes();
}
@RetryingTest(3)
void shouldPass() {
flakyObject.failsFirstTwoTimes();
}
}
Third time's
the charm eh?
Stdin
@Test
@StdIo({"foo", "bar", "baz"})
void shouldReadFromStandardInput() throws IOException {
var reader = new BufferedReader(new InputStreamReader(System.in));
assertThat(reader.readLine()).isEqualTo("foo");
assertThat(reader.readLine()).isEqualTo("bar");
assertThat(reader.readLine()).isEqualTo("baz");
}
@Test
@StdIo({"1", "2"})
void shouldCaptureStdIn(StdIn stdIn) throws IOException {
var reader = new BufferedReader(new InputStreamReader(System.in));
reader.readLine();
reader.readLine();
assertThat(stdIn.capturedLines()).containsExactly("1", "2");
}
& Stdout
@Test
@StdIo
void shouldInterceptStandardOutput(StdOut stdOut) {
System.out.println("The answer is 24");
System.out.println("No, the real answer is always 42");
assertThat(stdOut.capturedLines()).containsExactly(
"The answer is 24",
"No, the real answer is always 42"
);
}
How long did that take?
class StopwatchTest {
@RepeatedTest(value = 10)
@Stopwatch
void shouldReportElapsedTime() { ... }
}
Elapsed time will be published
via a TestReporter, so that a
TestExecutionListener can
receive it
Out on the range...
Create ranges of ints, longs, etc. as test input
Use with parameterized tests
Specify lower and upper bounds
Upper bound can be inclusive or exclusive
@ParameterizedTest
@IntRangeSource(from = 0, to = 10)
void shouldGenerateIntegers(int value) {
assertThat(value).isBetween(0, 9);
failIfSeeUpperBound(value, 10);
}
'from' is inclusive;
'to' is exclusive by default
@IntRangeSource(from = 0, to = 10)
@ParameterizedTest
@LongRangeSource(from = -100_000, to = 100_000, step = 5_000)
void shouldAllowChangingTheStep(long value) {
assertThat(value).isBetween(-100_000L, 95_000L);
failIfSeeUpperBound(value, 100_000);
}
change difference between
generated values using 'step'
@ParameterizedTest
@DoubleRangeSource(from = 0.0, to = 10.0, step = 0.5, closed = true)
void shouldAllowClosedRanges(double value) {
assertThat(value).isBetween(0.0, 10.0);
}
make a closed range
The best vintage?
Pioneer's vintage @Test replaces JUnit 4 @Test...
...but marks the method as a Jupiter test
Supports expected and timeout parameters
@CartesianProductTest
Generate cartesian product of all inputs
Use @CartesianProductTest instead of
Jupiter @Test annotations
Pioneer provides custom argument sources
You can also provide a custom @ArgumentsSource
@CartesianProductTest({"0", "1"})
void shouldProduceAllCombinationsOfThree(String x,
String y,
String z) {
List.of(x, y, z).forEach(this::assertIsZeroOrOne);
}
specify a String array
produces all input
combinations
(2 to the 3rd
power in this
example)
@CartesianProductTest
@CartesianValueSource(strings = {"A", "B", "C"})
@CartesianValueSource(ints = {1, 2, 3})
@CartesianEnumSource(value = Result.class,
names = {"SKIPPED"},
mode = CartesianEnumSource.Mode.EXCLUDE)
void shouldProduceAllCombinationsForCartesianValueSources(
String x, int y, Result z) {
assertThat(equalsAny(x, "A", "B", "C")).isTrue();
assertThat(Range.closed(1, 3).contains(y)).isTrue();
assertThat(z).isNotNull();
}
Analogous to Jupiter's
@ValueSource and
@EnumSource
Using @CartesianValueSource
and @CartesianEnumSource
Danger Will Robinson!
You must use Pioneer's @CartesianXyxSource
with @CartesianProductTest
Trying to use plain Jupiter sources like
@ValueSource will result in exceptions
@CartesianProductTest
@IntRangeSource(from = 1, to = 4, closed = true)
@LongRangeSource(from = 1000, to = 1005, closed = true)
void shouldWorkWithRangeSources(int x, long y) {
assertThat(Range.closed(1, 4).contains(x)).isTrue();
assertThat(Range.closed(1000L, 1005L).contains(y)).isTrue();
}
Also works with
Pioneer range
sources
Using several of Pioneer's
@XyzRangeSource annotations
with @CartesianProductTest
Can also create custom factory methods
Cartesian Argument Factories
Must be static & return CartesianProductTest.Sets
Use naming convention or factory parameter
in @CartesianProductTest
@CartesianProductTest(factory = "customProduct")
void shouldAllowCustomArgumentFactory(
String greek, Result result, int level) {
assertThat(equalsAny(greek, "Alpha", "Beta", "Gamma")).isTrue();
assertThat(result).isNotNull();
assertThat(Range.closedOpen(0, 5).contains(level)).isTrue();
}
static CartesianProductTest.Sets customProduct() {
return new CartesianProductTest.Sets()
.add("Alpha", "Beta", "Gamma")
.add((Object[]) Result.values())
.addAll(Stream.iterate(0, val -> val + 1).limit(5));
}
Instead, we could name the test
'customProduct' (but, I think using
factory is more understandable)
Using our custom
factory with
@CartesianProductTest
(there are 3 * 3 * 5 = 45 input combos)
Wrap Up
Pioneer has some cool things (fact)
Pioneer has some useful things (fact)
You should probably use them (opinion)
Example Code
https://github.com/sleberknight/junit-pioneering-presentation-code
References
JUnit Pioneer website
https://junit-pioneer.org
On GitHub
https://github.com/junit-pioneer/junit-pioneer
IETF BCP 47
https://tools.ietf.org/html/bcp47
IETF language tag (wikipedia)
https://en.wikipedia.org/wiki/IETF_language_tag
JDK 11 Locale
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Locale.html
Local Helper website
https://lh.2xlibre.net
Photos & Images
An artist's impression of a Pioneer spacecraft on its way to interstellar space
https://commons.wikimedia.org/wiki/File:An_artist%27s_impression_of_a_Pioneer_spacecraft_on_its_way_to_interstellar_space.jpg
One more thing...
https://www.flickr.com/photos/mathoov/4681491052
https://creativecommons.org/licenses/by-nc-nd/2.0/
My Info
sleberknight at
fortitudetec.com
www.fortitudetec.com
@sleberknight
scott.leberknight at
gmail

More Related Content

What's hot

Junit and testNG
Junit and testNGJunit and testNG
Junit and testNG
Марія Русин
 
Junit
JunitJunit
JUnit- A Unit Testing Framework
JUnit- A Unit Testing FrameworkJUnit- A Unit Testing Framework
JUnit- A Unit Testing Framework
Onkar Deshpande
 
Java Unit Testing
Java Unit TestingJava Unit Testing
Java Unit Testing
Nayanda Haberty
 
JUnit 5 - The Next Generation of JUnit - Ted's Tool Time
JUnit 5 - The Next Generation of JUnit - Ted's Tool TimeJUnit 5 - The Next Generation of JUnit - Ted's Tool Time
JUnit 5 - The Next Generation of JUnit - Ted's Tool Time
Ted Vinke
 
JUnit & Mockito, first steps
JUnit & Mockito, first stepsJUnit & Mockito, first steps
JUnit & Mockito, first steps
Renato Primavera
 
JUNit Presentation
JUNit PresentationJUNit Presentation
JUNit Presentation
Animesh Kumar
 
Unit testing with JUnit
Unit testing with JUnitUnit testing with JUnit
Unit testing with JUnit
Pokpitch Patcharadamrongkul
 
JUnit 4
JUnit 4JUnit 4
JUnit 4
Sunil OS
 
Advanced junit and mockito
Advanced junit and mockitoAdvanced junit and mockito
Advanced junit and mockito
Mathieu Carbou
 
Introduction To J unit
Introduction To J unitIntroduction To J unit
Introduction To J unit
Olga Extone
 
J Unit
J UnitJ Unit
JUnit Presentation
JUnit PresentationJUnit Presentation
JUnit Presentation
priya_trivedi
 
Introduction to JUnit
Introduction to JUnitIntroduction to JUnit
Introduction to JUnit
Devvrat Shukla
 
Unit testing with JUnit
Unit testing with JUnitUnit testing with JUnit
Unit testing with JUnit
Thomas Zimmermann
 
Unit Testing in Java
Unit Testing in JavaUnit Testing in Java
Unit Testing in Java
Ahmed M. Gomaa
 
Test driven development - JUnit basics and best practices
Test driven development - JUnit basics and best practicesTest driven development - JUnit basics and best practices
Test driven development - JUnit basics and best practices
Narendra Pathai
 
Unit testing with Junit
Unit testing with JunitUnit testing with Junit
Unit testing with Junit
Valerio Maggio
 
Junit
JunitJunit
testng
testngtestng

What's hot (20)

Junit and testNG
Junit and testNGJunit and testNG
Junit and testNG
 
Junit
JunitJunit
Junit
 
JUnit- A Unit Testing Framework
JUnit- A Unit Testing FrameworkJUnit- A Unit Testing Framework
JUnit- A Unit Testing Framework
 
Java Unit Testing
Java Unit TestingJava Unit Testing
Java Unit Testing
 
JUnit 5 - The Next Generation of JUnit - Ted's Tool Time
JUnit 5 - The Next Generation of JUnit - Ted's Tool TimeJUnit 5 - The Next Generation of JUnit - Ted's Tool Time
JUnit 5 - The Next Generation of JUnit - Ted's Tool Time
 
JUnit & Mockito, first steps
JUnit & Mockito, first stepsJUnit & Mockito, first steps
JUnit & Mockito, first steps
 
JUNit Presentation
JUNit PresentationJUNit Presentation
JUNit Presentation
 
Unit testing with JUnit
Unit testing with JUnitUnit testing with JUnit
Unit testing with JUnit
 
JUnit 4
JUnit 4JUnit 4
JUnit 4
 
Advanced junit and mockito
Advanced junit and mockitoAdvanced junit and mockito
Advanced junit and mockito
 
Introduction To J unit
Introduction To J unitIntroduction To J unit
Introduction To J unit
 
J Unit
J UnitJ Unit
J Unit
 
JUnit Presentation
JUnit PresentationJUnit Presentation
JUnit Presentation
 
Introduction to JUnit
Introduction to JUnitIntroduction to JUnit
Introduction to JUnit
 
Unit testing with JUnit
Unit testing with JUnitUnit testing with JUnit
Unit testing with JUnit
 
Unit Testing in Java
Unit Testing in JavaUnit Testing in Java
Unit Testing in Java
 
Test driven development - JUnit basics and best practices
Test driven development - JUnit basics and best practicesTest driven development - JUnit basics and best practices
Test driven development - JUnit basics and best practices
 
Unit testing with Junit
Unit testing with JunitUnit testing with Junit
Unit testing with Junit
 
Junit
JunitJunit
Junit
 
testng
testngtestng
testng
 

Similar to JUnit Pioneer

Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unit
liminescence
 
Unittesting JavaScript with Evidence
Unittesting JavaScript with EvidenceUnittesting JavaScript with Evidence
Unittesting JavaScript with Evidence
Tobie Langel
 
Testing Java Code Effectively
Testing Java Code EffectivelyTesting Java Code Effectively
Testing Java Code Effectively
Andres Almiray
 
Junit_.pptx
Junit_.pptxJunit_.pptx
Junit_.pptx
Suman Sourav
 
Js 单元测试框架介绍
Js 单元测试框架介绍Js 单元测试框架介绍
Js 单元测试框架介绍
louieuser
 
Understanding JavaScript Testing
Understanding JavaScript TestingUnderstanding JavaScript Testing
Understanding JavaScript Testing
jeresig
 
Introduction to nsubstitute
Introduction to nsubstituteIntroduction to nsubstitute
Introduction to nsubstitute
Suresh Loganatha
 
Apex Testing and Best Practices
Apex Testing and Best PracticesApex Testing and Best Practices
Apex Testing and Best Practices
Jitendra Zaa
 
Google guava
Google guavaGoogle guava
Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4
jeresig
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
Tomek Kaczanowski
 
Confitura 2012 Bad Tests, Good Tests
Confitura 2012 Bad Tests, Good TestsConfitura 2012 Bad Tests, Good Tests
Confitura 2012 Bad Tests, Good Tests
Tomek Kaczanowski
 
Junit 5 - Maior e melhor
Junit 5 - Maior e melhorJunit 5 - Maior e melhor
Junit 5 - Maior e melhor
Tiago de Freitas Lima
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
Tomek Kaczanowski
 
EVERYTHING ABOUT STATIC CODE ANALYSIS FOR A JAVA PROGRAMMER
EVERYTHING ABOUT STATIC CODE ANALYSIS FOR A JAVA PROGRAMMEREVERYTHING ABOUT STATIC CODE ANALYSIS FOR A JAVA PROGRAMMER
EVERYTHING ABOUT STATIC CODE ANALYSIS FOR A JAVA PROGRAMMER
Andrey Karpov
 
STAMP Descartes Presentation
STAMP Descartes PresentationSTAMP Descartes Presentation
STAMP Descartes Presentation
STAMP Project
 
Unit & Automation Testing in Android - Stanislav Gatsev, Melon
Unit & Automation Testing in Android - Stanislav Gatsev, MelonUnit & Automation Testing in Android - Stanislav Gatsev, Melon
Unit & Automation Testing in Android - Stanislav Gatsev, Melon
beITconference
 
JUnit5 and TestContainers
JUnit5 and TestContainersJUnit5 and TestContainers
JUnit5 and TestContainers
Sunghyouk Bae
 
Test-driven Development for TYPO3
Test-driven Development for TYPO3Test-driven Development for TYPO3
Test-driven Development for TYPO3
Oliver Klee
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
javatwo2011
 

Similar to JUnit Pioneer (20)

Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unit
 
Unittesting JavaScript with Evidence
Unittesting JavaScript with EvidenceUnittesting JavaScript with Evidence
Unittesting JavaScript with Evidence
 
Testing Java Code Effectively
Testing Java Code EffectivelyTesting Java Code Effectively
Testing Java Code Effectively
 
Junit_.pptx
Junit_.pptxJunit_.pptx
Junit_.pptx
 
Js 单元测试框架介绍
Js 单元测试框架介绍Js 单元测试框架介绍
Js 单元测试框架介绍
 
Understanding JavaScript Testing
Understanding JavaScript TestingUnderstanding JavaScript Testing
Understanding JavaScript Testing
 
Introduction to nsubstitute
Introduction to nsubstituteIntroduction to nsubstitute
Introduction to nsubstitute
 
Apex Testing and Best Practices
Apex Testing and Best PracticesApex Testing and Best Practices
Apex Testing and Best Practices
 
Google guava
Google guavaGoogle guava
Google guava
 
Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
 
Confitura 2012 Bad Tests, Good Tests
Confitura 2012 Bad Tests, Good TestsConfitura 2012 Bad Tests, Good Tests
Confitura 2012 Bad Tests, Good Tests
 
Junit 5 - Maior e melhor
Junit 5 - Maior e melhorJunit 5 - Maior e melhor
Junit 5 - Maior e melhor
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
 
EVERYTHING ABOUT STATIC CODE ANALYSIS FOR A JAVA PROGRAMMER
EVERYTHING ABOUT STATIC CODE ANALYSIS FOR A JAVA PROGRAMMEREVERYTHING ABOUT STATIC CODE ANALYSIS FOR A JAVA PROGRAMMER
EVERYTHING ABOUT STATIC CODE ANALYSIS FOR A JAVA PROGRAMMER
 
STAMP Descartes Presentation
STAMP Descartes PresentationSTAMP Descartes Presentation
STAMP Descartes Presentation
 
Unit & Automation Testing in Android - Stanislav Gatsev, Melon
Unit & Automation Testing in Android - Stanislav Gatsev, MelonUnit & Automation Testing in Android - Stanislav Gatsev, Melon
Unit & Automation Testing in Android - Stanislav Gatsev, Melon
 
JUnit5 and TestContainers
JUnit5 and TestContainersJUnit5 and TestContainers
JUnit5 and TestContainers
 
Test-driven Development for TYPO3
Test-driven Development for TYPO3Test-driven Development for TYPO3
Test-driven Development for TYPO3
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 

More from Scott Leberknight

JShell & ki
JShell & kiJShell & ki
JShell & ki
Scott Leberknight
 
JDKs 10 to 14 (and beyond)
JDKs 10 to 14 (and beyond)JDKs 10 to 14 (and beyond)
JDKs 10 to 14 (and beyond)
Scott Leberknight
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
Scott Leberknight
 
SDKMAN!
SDKMAN!SDKMAN!
AWS Lambda
AWS LambdaAWS Lambda
AWS Lambda
Scott Leberknight
 
Dropwizard
DropwizardDropwizard
Dropwizard
Scott Leberknight
 
RESTful Web Services with Jersey
RESTful Web Services with JerseyRESTful Web Services with Jersey
RESTful Web Services with Jersey
Scott Leberknight
 
httpie
httpiehttpie
jps & jvmtop
jps & jvmtopjps & jvmtop
jps & jvmtop
Scott Leberknight
 
Cloudera Impala, updated for v1.0
Cloudera Impala, updated for v1.0Cloudera Impala, updated for v1.0
Cloudera Impala, updated for v1.0
Scott Leberknight
 
Java 8 Lambda Expressions
Java 8 Lambda ExpressionsJava 8 Lambda Expressions
Java 8 Lambda Expressions
Scott Leberknight
 
Google Guava
Google GuavaGoogle Guava
Google Guava
Scott Leberknight
 
Cloudera Impala
Cloudera ImpalaCloudera Impala
Cloudera Impala
Scott Leberknight
 
iOS
iOSiOS
Apache ZooKeeper
Apache ZooKeeperApache ZooKeeper
Apache ZooKeeper
Scott Leberknight
 
HBase Lightning Talk
HBase Lightning TalkHBase Lightning Talk
HBase Lightning Talk
Scott Leberknight
 
Hadoop
HadoopHadoop
wtf is in Java/JDK/wtf7?
wtf is in Java/JDK/wtf7?wtf is in Java/JDK/wtf7?
wtf is in Java/JDK/wtf7?
Scott Leberknight
 
CoffeeScript
CoffeeScriptCoffeeScript
CoffeeScript
Scott Leberknight
 
Polyglot Persistence
Polyglot PersistencePolyglot Persistence
Polyglot Persistence
Scott Leberknight
 

More from Scott Leberknight (20)

JShell & ki
JShell & kiJShell & ki
JShell & ki
 
JDKs 10 to 14 (and beyond)
JDKs 10 to 14 (and beyond)JDKs 10 to 14 (and beyond)
JDKs 10 to 14 (and beyond)
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
 
SDKMAN!
SDKMAN!SDKMAN!
SDKMAN!
 
AWS Lambda
AWS LambdaAWS Lambda
AWS Lambda
 
Dropwizard
DropwizardDropwizard
Dropwizard
 
RESTful Web Services with Jersey
RESTful Web Services with JerseyRESTful Web Services with Jersey
RESTful Web Services with Jersey
 
httpie
httpiehttpie
httpie
 
jps & jvmtop
jps & jvmtopjps & jvmtop
jps & jvmtop
 
Cloudera Impala, updated for v1.0
Cloudera Impala, updated for v1.0Cloudera Impala, updated for v1.0
Cloudera Impala, updated for v1.0
 
Java 8 Lambda Expressions
Java 8 Lambda ExpressionsJava 8 Lambda Expressions
Java 8 Lambda Expressions
 
Google Guava
Google GuavaGoogle Guava
Google Guava
 
Cloudera Impala
Cloudera ImpalaCloudera Impala
Cloudera Impala
 
iOS
iOSiOS
iOS
 
Apache ZooKeeper
Apache ZooKeeperApache ZooKeeper
Apache ZooKeeper
 
HBase Lightning Talk
HBase Lightning TalkHBase Lightning Talk
HBase Lightning Talk
 
Hadoop
HadoopHadoop
Hadoop
 
wtf is in Java/JDK/wtf7?
wtf is in Java/JDK/wtf7?wtf is in Java/JDK/wtf7?
wtf is in Java/JDK/wtf7?
 
CoffeeScript
CoffeeScriptCoffeeScript
CoffeeScript
 
Polyglot Persistence
Polyglot PersistencePolyglot Persistence
Polyglot Persistence
 

Recently uploaded

“I’m still / I’m still / Chaining from the Block”
“I’m still / I’m still / Chaining from the Block”“I’m still / I’m still / Chaining from the Block”
“I’m still / I’m still / Chaining from the Block”
Claudio Di Ciccio
 
Infrastructure Challenges in Scaling RAG with Custom AI models
Infrastructure Challenges in Scaling RAG with Custom AI modelsInfrastructure Challenges in Scaling RAG with Custom AI models
Infrastructure Challenges in Scaling RAG with Custom AI models
Zilliz
 
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
Edge AI and Vision Alliance
 
June Patch Tuesday
June Patch TuesdayJune Patch Tuesday
June Patch Tuesday
Ivanti
 
20240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 202420240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 2024
Matthew Sinclair
 
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Malak Abu Hammad
 
How to Get CNIC Information System with Paksim Ga.pptx
How to Get CNIC Information System with Paksim Ga.pptxHow to Get CNIC Information System with Paksim Ga.pptx
How to Get CNIC Information System with Paksim Ga.pptx
danishmna97
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
Octavian Nadolu
 
Taking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdfTaking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdf
ssuserfac0301
 
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Speck&Tech
 
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAUHCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
panagenda
 
GenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizationsGenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizations
kumardaparthi1024
 
Mind map of terminologies used in context of Generative AI
Mind map of terminologies used in context of Generative AIMind map of terminologies used in context of Generative AI
Mind map of terminologies used in context of Generative AI
Kumud Singh
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
shyamraj55
 
GraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracyGraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracy
Tomaz Bratanic
 
20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
Matthew Sinclair
 
Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024
Jason Packer
 
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success StoryDriving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Safe Software
 
UI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentationUI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentation
Wouter Lemaire
 
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development ProvidersYour One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
akankshawande
 

Recently uploaded (20)

“I’m still / I’m still / Chaining from the Block”
“I’m still / I’m still / Chaining from the Block”“I’m still / I’m still / Chaining from the Block”
“I’m still / I’m still / Chaining from the Block”
 
Infrastructure Challenges in Scaling RAG with Custom AI models
Infrastructure Challenges in Scaling RAG with Custom AI modelsInfrastructure Challenges in Scaling RAG with Custom AI models
Infrastructure Challenges in Scaling RAG with Custom AI models
 
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
 
June Patch Tuesday
June Patch TuesdayJune Patch Tuesday
June Patch Tuesday
 
20240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 202420240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 2024
 
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
 
How to Get CNIC Information System with Paksim Ga.pptx
How to Get CNIC Information System with Paksim Ga.pptxHow to Get CNIC Information System with Paksim Ga.pptx
How to Get CNIC Information System with Paksim Ga.pptx
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
 
Taking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdfTaking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdf
 
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
 
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAUHCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
 
GenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizationsGenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizations
 
Mind map of terminologies used in context of Generative AI
Mind map of terminologies used in context of Generative AIMind map of terminologies used in context of Generative AI
Mind map of terminologies used in context of Generative AI
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
 
GraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracyGraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracy
 
20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
 
Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024
 
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success StoryDriving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success Story
 
UI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentationUI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentation
 
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development ProvidersYour One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
 

JUnit Pioneer

  • 2. “JUnit Pioneer is an extension pack for JUnit 5 or, to be more precise, for the Jupiter engine”
  • 3. Recap: JUnit Extensions Provide a way to extend JUnit’s core features Well-defined extension API and lifecycle
  • 4. Simple Example @ParameterizedTest @RandomIntSource(min = 5, max = 10, count = 50) void shouldDoSomethingWithRandomInts(int value) { // test code... } Injecting method parameters into tests Example from https://github.com/kiwiproject/kiwi-test/
  • 5. @RegisterExtension static final CuratorTestingServerExtension ZK_TEST_SERVER = new CuratorTestingServerExtension(); Another Example Start and stop a ZooKeeper server before & after tests Example from https://github.com/kiwiproject/kiwi-test/
  • 6. Pioneer Extensions A collection of useful Jupiter extensions Easy to integrate into your tests
  • 7. Pioneer Extensions Cartesian product Locale & TimeZone Environment Vars Range sources Issue Info Disabling tests Report entries Stdin & Stdout System props Vintage tests Stopwatch! * as of 2021-03-04 *
  • 8. System Properties @SetSystemProperty(key = "user", value = "bob") @SetSystemProperty(key = "id", value = "42") class SystemPropertyTest { @Test @ClearSystemProperty(key = "user") void testWithoutUserProperty() { ... } @Test @SetSystemProperty(key = "pwd", value = "12345") void anotherTest() { ... } }
  • 9. Environment Variables @SetEnvironmentVariable(key = "PWD", value="/tmp") @ClearEnvironmentVariable(key = "PS1") class EnvVarsTest { @ClearEnvironmentVariable(key = "PWD") void testWithoutWorkingDirEnvVar() { ... } @SetEnvironmentVariable(key = "USER", value = "alice") void testWithCustomUser() { ... } }
  • 10. Default TimeZone @DefaultTimeZone("America/Mexico_City") class DefaultTimeZoneTest { @Test void testInMexicoCity() { assertThat(TimeZone.getDefault()).isEqualTo( TimeZone.getTimeZone("America/Mexico_City")); } @Test @DefaultTimeZone("Mexico/BajaSur") void testInBajaSur() { ... } }
  • 11. Default Locale @DefaultLocale("es") class DefaultLocaleTest { @Test void testWithDefaultFromClass() { assertThat(Locale.getDefault()) .isEqualTo(new Locale("es")); } @Test @DefaultLocale(language = "nan", country = "TW", variant = "Hant") void testUsingAllAttributes() { assertThat(Locale.getDefault()).isEqualTo( new Locale("nan", "TW", "Hant")); } }
  • 12. Having Issues? public class SystemOutIssueProcessor implements IssueProcessor { @Override public void processTestResults(List<IssueTestSuite> suites) { // do something with each test suite } } @Test @Issue("JUPI-1234") void shouldFix1234() { // test code for issue JUPI-1234 } #1 Annotate #2 Define processor
  • 14. I need those TPS reports... class ReportEntriesTest { @Test @ReportEntry("I'm Going To Need Those TPS Reports ASAP") void testWithReportEntry() { ... } @Test @ReportEntry(key = "result", value = "success", when = PublishCondition.ON_SUCCESS) @ReportEntry(key = "result", value = "failed", when = ReportEntry.PublishCondition.ON_FAILURE) void testWithConditionalEntries() { ... } } #1 Annotate
  • 15. public class SimpleTestExecutionListener implements TestExecutionListener { ... } #2 Create a JUnit TestExecutionListener com.fortitudetec.junit.pioneering.SimpleTestExecutionListener META-INF/services/org.junit.platform.launcher.TestExecutionListener #3 Register implementation
  • 16. Disabling on display name Selectively disable parameterized tests Specify substrings and/or regex to match... ...and disable any tests with a matching name
  • 17. @DisableIfDisplayName(contains = "42") @ParameterizedTest(name = "Test scenario {0}") @ValueSource(ints = {1, 24, 42, 84, 168, 420, 4200, 4242, 17640}) void shouldDisableUsingStringContains(int value) { if (String.valueOf(value).contains("42")) { fail("Should not have received %s", value); } } Using a substring to specify disabling condition matching against the generated test name
  • 18. @DisableIfDisplayName(matches = ".*d{3}-d{2}-d{4}.*") @ParameterizedTest(name = "Test scenario {0}") @ValueSource(strings = { "42", "123-45-6789", "400", "234-56-7890", "888" }) void shouldDisableUsingRegex(String value) { failIfContainsAny(value, "123-45-6789", "234-56-7890"); } Or use a regular expression
  • 19. @DisableIfDisplayName(contains = {"42", "84"}, matches = ".*d{3}-d{2}-d{4}.*") @ParameterizedTest(name = "Test scenario {0}") @ValueSource(strings = { "24", "42", "123-45-6789", "400", "234-56-7890", "888" }) void shouldDisableUsingContainsAndMatches(String value) { failIfContainsAny(value, "42", "84", "123-45-6789", "234-56-7890"); } or use both...
  • 20. @DisableIfDisplayName(matches = ".*[0-9][3|5]") @ParameterizedTest(name = "Product: FTCH-000-{0}") @RandomIntSource(min = 0, max = 1_000, count = 500) void shouldDisableWhenProductCodeEndsWith_X3_Or_X5(int code) { var codeString = String.valueOf(code); if (PRODUCT_NUMBER_PATTERN.matcher(codeString).matches()) { fail("Should not have received %d", code); } } Combine a regex with randomized test input @RandomIntSource from https://github.com/kiwiproject/kiwi-test/
  • 21.
  • 22. Do, or do not, there is no try class RetryingAnnotationTest { @RetryingTest(2) void shouldFail() { flakyObject.failsFirstTwoTimes(); } @RetryingTest(3) void shouldPass() { flakyObject.failsFirstTwoTimes(); } }
  • 24. Stdin @Test @StdIo({"foo", "bar", "baz"}) void shouldReadFromStandardInput() throws IOException { var reader = new BufferedReader(new InputStreamReader(System.in)); assertThat(reader.readLine()).isEqualTo("foo"); assertThat(reader.readLine()).isEqualTo("bar"); assertThat(reader.readLine()).isEqualTo("baz"); } @Test @StdIo({"1", "2"}) void shouldCaptureStdIn(StdIn stdIn) throws IOException { var reader = new BufferedReader(new InputStreamReader(System.in)); reader.readLine(); reader.readLine(); assertThat(stdIn.capturedLines()).containsExactly("1", "2"); }
  • 25. & Stdout @Test @StdIo void shouldInterceptStandardOutput(StdOut stdOut) { System.out.println("The answer is 24"); System.out.println("No, the real answer is always 42"); assertThat(stdOut.capturedLines()).containsExactly( "The answer is 24", "No, the real answer is always 42" ); }
  • 26. How long did that take? class StopwatchTest { @RepeatedTest(value = 10) @Stopwatch void shouldReportElapsedTime() { ... } } Elapsed time will be published via a TestReporter, so that a TestExecutionListener can receive it
  • 27. Out on the range... Create ranges of ints, longs, etc. as test input Use with parameterized tests Specify lower and upper bounds Upper bound can be inclusive or exclusive
  • 28. @ParameterizedTest @IntRangeSource(from = 0, to = 10) void shouldGenerateIntegers(int value) { assertThat(value).isBetween(0, 9); failIfSeeUpperBound(value, 10); } 'from' is inclusive; 'to' is exclusive by default
  • 30. @ParameterizedTest @LongRangeSource(from = -100_000, to = 100_000, step = 5_000) void shouldAllowChangingTheStep(long value) { assertThat(value).isBetween(-100_000L, 95_000L); failIfSeeUpperBound(value, 100_000); } change difference between generated values using 'step'
  • 31. @ParameterizedTest @DoubleRangeSource(from = 0.0, to = 10.0, step = 0.5, closed = true) void shouldAllowClosedRanges(double value) { assertThat(value).isBetween(0.0, 10.0); } make a closed range
  • 32. The best vintage? Pioneer's vintage @Test replaces JUnit 4 @Test... ...but marks the method as a Jupiter test Supports expected and timeout parameters
  • 33.
  • 35. Generate cartesian product of all inputs Use @CartesianProductTest instead of Jupiter @Test annotations Pioneer provides custom argument sources You can also provide a custom @ArgumentsSource
  • 36. @CartesianProductTest({"0", "1"}) void shouldProduceAllCombinationsOfThree(String x, String y, String z) { List.of(x, y, z).forEach(this::assertIsZeroOrOne); } specify a String array produces all input combinations (2 to the 3rd power in this example)
  • 37. @CartesianProductTest @CartesianValueSource(strings = {"A", "B", "C"}) @CartesianValueSource(ints = {1, 2, 3}) @CartesianEnumSource(value = Result.class, names = {"SKIPPED"}, mode = CartesianEnumSource.Mode.EXCLUDE) void shouldProduceAllCombinationsForCartesianValueSources( String x, int y, Result z) { assertThat(equalsAny(x, "A", "B", "C")).isTrue(); assertThat(Range.closed(1, 3).contains(y)).isTrue(); assertThat(z).isNotNull(); } Analogous to Jupiter's @ValueSource and @EnumSource
  • 39. Danger Will Robinson! You must use Pioneer's @CartesianXyxSource with @CartesianProductTest Trying to use plain Jupiter sources like @ValueSource will result in exceptions
  • 40. @CartesianProductTest @IntRangeSource(from = 1, to = 4, closed = true) @LongRangeSource(from = 1000, to = 1005, closed = true) void shouldWorkWithRangeSources(int x, long y) { assertThat(Range.closed(1, 4).contains(x)).isTrue(); assertThat(Range.closed(1000L, 1005L).contains(y)).isTrue(); } Also works with Pioneer range sources
  • 41. Using several of Pioneer's @XyzRangeSource annotations with @CartesianProductTest
  • 42. Can also create custom factory methods Cartesian Argument Factories Must be static & return CartesianProductTest.Sets Use naming convention or factory parameter in @CartesianProductTest
  • 43. @CartesianProductTest(factory = "customProduct") void shouldAllowCustomArgumentFactory( String greek, Result result, int level) { assertThat(equalsAny(greek, "Alpha", "Beta", "Gamma")).isTrue(); assertThat(result).isNotNull(); assertThat(Range.closedOpen(0, 5).contains(level)).isTrue(); } static CartesianProductTest.Sets customProduct() { return new CartesianProductTest.Sets() .add("Alpha", "Beta", "Gamma") .add((Object[]) Result.values()) .addAll(Stream.iterate(0, val -> val + 1).limit(5)); } Instead, we could name the test 'customProduct' (but, I think using factory is more understandable)
  • 44. Using our custom factory with @CartesianProductTest (there are 3 * 3 * 5 = 45 input combos)
  • 45. Wrap Up Pioneer has some cool things (fact) Pioneer has some useful things (fact) You should probably use them (opinion)
  • 47. References JUnit Pioneer website https://junit-pioneer.org On GitHub https://github.com/junit-pioneer/junit-pioneer IETF BCP 47 https://tools.ietf.org/html/bcp47 IETF language tag (wikipedia) https://en.wikipedia.org/wiki/IETF_language_tag JDK 11 Locale https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Locale.html Local Helper website https://lh.2xlibre.net
  • 48. Photos & Images An artist's impression of a Pioneer spacecraft on its way to interstellar space https://commons.wikimedia.org/wiki/File:An_artist%27s_impression_of_a_Pioneer_spacecraft_on_its_way_to_interstellar_space.jpg One more thing... https://www.flickr.com/photos/mathoov/4681491052 https://creativecommons.org/licenses/by-nc-nd/2.0/