/24@yegor256 1
Yegor Bugayenko
Object-Oriented Flavor for
JUnit Tests
/24@yegor256 2
F.I.R.S.T.:
Fast
Isolated/Independent
Repeatable
Self-validating
Thorough and Timely
— Robert Martin

Clean Code
/24@yegor256 3
There are books:
Kent Beck, Test-Driven Development by Example, 2000.
Johannes Link, Unit testing in Java, 2003.
Ted Husted and Vincent Massol, JUnit in Action, 2003.
Andy Hunt and David Thomas, Pragmatic Unit Testing in C# with NUnit, 2003.
Gerard Meszaros, XUnit Test Patterns: Refactoring Test Code, 2007.
Nat Pryce and Steve Freeman, Growing Object-Oriented Software: Guided by Tests, 2009.
Lasse Koskela, Effective Unit Testing: A Guide for Java Developers, 2013.
J. B. Rainsberger, JUnit recipes, 2014.
Sujoy Acharya, Mastering Unit Testing Using Mockito and JUnit, 2014.
/24@yegor256 4
Unit testing anti-patterns:
Happy Path Tests
Validation and Boundary
Easy Tests
The Giant
The Cuckoo
The Conjoined Twins
The Slow Poke
Anal Probe
Test It All
Line Hitter
Wait and See
The Silent Catcher
Chain Gang
The Mockery
The Free Ride
The Local Hero
Wet Floor
The Flickering Test
The Environmental Vandal
Second Class Citizens
The Secret Catcher
Logic in Tests
Code Matching
Misleading Tests
Not Asserting
Asserting on Not-Null
/24@yegor256 5
I believe that one anti-pattern

is still missing:
The algorithm
Provided we’re talking about OOP
/24@yegor256 6
xUnit test method is

basically a procedure.
/24@yegor256 7
class BookTest {
@Test
void testWorksAsExpected() {
Book book = new Book();
book.setLanguage(Locale.RUSSIAN);
book.setEncoding(“UTF-8”);
book.setTitle(“Дон Кихот”);
assertTrue(book.getURL().contains(“%D0%94%D0%BE%D0”));
}
}
1.Algorithm
2.Output
3.Assertion
/24@yegor256 8
A test method must have nothing
but a single statement:
assertThat()
Provided we’re talking about Java
/24@yegor256 9
class BookTest {
@Test
void testWorksAsExpected() {
// nothing goes here
assertThat(
new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN),
new HasURL(new StringContains(“%D0%94%D0%BE%D0”))
);
}
}
/24@yegor256 10
Assert that the book is similar
to a book that has a URL that
contains a string that is equal to
“%D0%94”.
assertThat(
new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN),
new HasURL(new StringContains(“%D0%94%”))
);
/24@yegor256 11
Hamcrest
By the way, this is the anagram for “matchers”.
/24@yegor256 12
Some of anti-patterns will just
disappear:
Happy Path Tests
Validation and Boundary
Easy Tests
The Giant
The Cuckoo
The Conjoined Twins
The Slow Poke
Anal Probe
Test It All
Line Hitter
Wait and See
The Silent Catcher
Chain Gang
The Mockery
The Free Ride
The Local Hero
Wet Floor
The Flickering Test
The Environmental Vandal
Second Class Citizens
The Secret Catcher
Logic in Tests
Code Matching
Misleading Tests
Not Asserting
Asserting on Not-Null
/24@yegor256 13
Obvious benefits:
Reduced complexity
Immutable objects
Shared “matchers”
Fake objects and no mocking
Better design (more object-oriented)
/24@yegor256
Reduced Complexity
14
void testWorksAsExpected() {
assertThat(
new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN),
new HasURL(new StringContains(“%D0%94%D0%BE%D0”))
);
}
No free ride
No multi asserts
No @Before/@After
/24@yegor256
Immutable Objects
15
void testWorksAsExpected() {
assertThat(
new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN),
new HasURL(new StringContains(“%D0%94%D0%BE%D0”))
);
}
No setters
No temporal coupling
No procedures
/24@yegor256
Shared Matchers
16
void testWorksAsExpected() {
assertThat(
new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN),
new HasURL(new StringContains(“%D0%94%D0%BE%D0”))
);
}
No private static methods
No duplication
Cleaner logs
/24@yegor256
No Mocking
17
void testWorksAsExpected() {
assertThat(
new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN),
new HasURL(new StringContains(“%D0%94%D0%BE%D0”))
);
}
No assumptions
No false positives
/24@yegor256 18
class BookTest {
@Test
void testWorksAsExpected() {
Book b = new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN);
Matcher m = new HasURL(new StringContains(“%D0%94%D0%BE%D0”));
assertThat(b, m);
}
}
1.Object
2.Matcher
3.Assertion
/24@yegor256 19
OpenJDK
RandomStreamTest.java
java.util.Random
for
/24@yegor256 20
@Test
public void testIntStream() {
final long seed = System.currentTimeMillis();
final Random r1 = new Random(seed);
final int[] a = new int[SIZE];
for (int i=0; i < SIZE; i++) {
a[i] = r1.nextInt();
}
final Random r2 = new Random(seed);
final int[] b = r2.ints().limit(SIZE).toArray();
assertEquals(a, b);
}
/24@yegor256 21
private static class ArrayFromRandom {
private final Random random;
ArrayFromRandom(Random r) {
this.random = r;
}
int[] toArray(int s) {
final int[] a = new int[s];
for (int i=0; i < s; i++) {
a[i] = this.random.nextInt();
}
return a;
}
}
@Test
public void testIntStream() {
final long seed = System.currentTimeMillis();
assertEquals(
new ArrayFromRandom(
new Random(seed)
).toArray(SIZE),
new Random(seed).ints().limit(SIZE).toArray()
);
}
/24@yegor256 22
@Test
public void testIntStream() {
assertEquals(
new ArrayFromRandom(
new Random(System.currentTimeMillis() as seed)
).toArray(SIZE),
new Random(seed).ints().limit(SIZE).toArray()
);
}
/24@yegor256 23
cactoos
www.cactoos.org
/24@yegor256 24
new Pipe(
new TextAsInput(“Hello, world!”),
new FileAsOutput(new File(“test.txt”))
).push();
/24@yegor256 25
@Test
public void canCopyTextToByteArray() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new Pipe(
new TextAsInput(“Hello, world!”),
new OutputStreamAsOutput(baos)
).push();
assertThat(
new String(baos.toByteArray()),
Matchers.containsString(“Hello”)
);
}
/24@yegor256 26
Input input = new TeeInput(
new TextAsInput(“Hello, world!”),
new FileAsOutput(new File(“test.txt”))
);
input.read();
new LengthOfInput(input).asValue();
6168af9
/24@yegor256 27
@Test
public void canCopyTextToByteArray() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
assertThat(
new InputAsText(
new TeeInput(
new TextAsInput(“Hello, world!”),
new OutputStreamAsOutput(baos)
)
),
Matchers.allOf(
equalTo(new String(baos.toByteArray())),
containsString(“Hello”)
)
);
}
/24@yegor256 28
@Test
public void canCopyTextToByteArray() {
ByteArrayOutputStream baos;
assertThat(
new InputAsText(
new TeeInput(
new TextAsInput(“Hello, world!”),
new OutputStreamAsOutput(baos = new ByteArrayOutputStream())
)
),
Matchers.allOf(
equalTo(new String(baos.toByteArray())),
containsString(“Hello”)
)
);
}
/24@yegor256 29
@Test
public void canCopyTextToByteArray() {
assertThat(
new InputAsText(
new TeeInput(
new TextAsInput(“Hello, world!”),
new OutputStreamAsOutput(
new ByteArrayOutputStream() as baos
)
)
),
Matchers.allOf(
equalTo(new String(baos.toByteArray())),
containsString(“Hello”)
)
);
}
/30@yegor256 30
The article on the blog:
/30@yegor256 31
shop@yegor256.com

Object-Oriented Flavor for JUnit Tests

  • 1.
  • 2.
  • 3.
    /24@yegor256 3 There arebooks: Kent Beck, Test-Driven Development by Example, 2000. Johannes Link, Unit testing in Java, 2003. Ted Husted and Vincent Massol, JUnit in Action, 2003. Andy Hunt and David Thomas, Pragmatic Unit Testing in C# with NUnit, 2003. Gerard Meszaros, XUnit Test Patterns: Refactoring Test Code, 2007. Nat Pryce and Steve Freeman, Growing Object-Oriented Software: Guided by Tests, 2009. Lasse Koskela, Effective Unit Testing: A Guide for Java Developers, 2013. J. B. Rainsberger, JUnit recipes, 2014. Sujoy Acharya, Mastering Unit Testing Using Mockito and JUnit, 2014.
  • 4.
    /24@yegor256 4 Unit testinganti-patterns: Happy Path Tests Validation and Boundary Easy Tests The Giant The Cuckoo The Conjoined Twins The Slow Poke Anal Probe Test It All Line Hitter Wait and See The Silent Catcher Chain Gang The Mockery The Free Ride The Local Hero Wet Floor The Flickering Test The Environmental Vandal Second Class Citizens The Secret Catcher Logic in Tests Code Matching Misleading Tests Not Asserting Asserting on Not-Null
  • 5.
    /24@yegor256 5 I believethat one anti-pattern
 is still missing: The algorithm Provided we’re talking about OOP
  • 6.
    /24@yegor256 6 xUnit testmethod is
 basically a procedure.
  • 7.
    /24@yegor256 7 class BookTest{ @Test void testWorksAsExpected() { Book book = new Book(); book.setLanguage(Locale.RUSSIAN); book.setEncoding(“UTF-8”); book.setTitle(“Дон Кихот”); assertTrue(book.getURL().contains(“%D0%94%D0%BE%D0”)); } } 1.Algorithm 2.Output 3.Assertion
  • 8.
    /24@yegor256 8 A testmethod must have nothing but a single statement: assertThat() Provided we’re talking about Java
  • 9.
    /24@yegor256 9 class BookTest{ @Test void testWorksAsExpected() { // nothing goes here assertThat( new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN), new HasURL(new StringContains(“%D0%94%D0%BE%D0”)) ); } }
  • 10.
    /24@yegor256 10 Assert thatthe book is similar to a book that has a URL that contains a string that is equal to “%D0%94”. assertThat( new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN), new HasURL(new StringContains(“%D0%94%”)) );
  • 11.
    /24@yegor256 11 Hamcrest By theway, this is the anagram for “matchers”.
  • 12.
    /24@yegor256 12 Some ofanti-patterns will just disappear: Happy Path Tests Validation and Boundary Easy Tests The Giant The Cuckoo The Conjoined Twins The Slow Poke Anal Probe Test It All Line Hitter Wait and See The Silent Catcher Chain Gang The Mockery The Free Ride The Local Hero Wet Floor The Flickering Test The Environmental Vandal Second Class Citizens The Secret Catcher Logic in Tests Code Matching Misleading Tests Not Asserting Asserting on Not-Null
  • 13.
    /24@yegor256 13 Obvious benefits: Reducedcomplexity Immutable objects Shared “matchers” Fake objects and no mocking Better design (more object-oriented)
  • 14.
    /24@yegor256 Reduced Complexity 14 void testWorksAsExpected(){ assertThat( new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN), new HasURL(new StringContains(“%D0%94%D0%BE%D0”)) ); } No free ride No multi asserts No @Before/@After
  • 15.
    /24@yegor256 Immutable Objects 15 void testWorksAsExpected(){ assertThat( new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN), new HasURL(new StringContains(“%D0%94%D0%BE%D0”)) ); } No setters No temporal coupling No procedures
  • 16.
    /24@yegor256 Shared Matchers 16 void testWorksAsExpected(){ assertThat( new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN), new HasURL(new StringContains(“%D0%94%D0%BE%D0”)) ); } No private static methods No duplication Cleaner logs
  • 17.
    /24@yegor256 No Mocking 17 void testWorksAsExpected(){ assertThat( new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN), new HasURL(new StringContains(“%D0%94%D0%BE%D0”)) ); } No assumptions No false positives
  • 18.
    /24@yegor256 18 class BookTest{ @Test void testWorksAsExpected() { Book b = new Book(“Дон Кихот”, “UTF-8”, Locale.RUSSIAN); Matcher m = new HasURL(new StringContains(“%D0%94%D0%BE%D0”)); assertThat(b, m); } } 1.Object 2.Matcher 3.Assertion
  • 19.
  • 20.
    /24@yegor256 20 @Test public voidtestIntStream() { final long seed = System.currentTimeMillis(); final Random r1 = new Random(seed); final int[] a = new int[SIZE]; for (int i=0; i < SIZE; i++) { a[i] = r1.nextInt(); } final Random r2 = new Random(seed); final int[] b = r2.ints().limit(SIZE).toArray(); assertEquals(a, b); }
  • 21.
    /24@yegor256 21 private staticclass ArrayFromRandom { private final Random random; ArrayFromRandom(Random r) { this.random = r; } int[] toArray(int s) { final int[] a = new int[s]; for (int i=0; i < s; i++) { a[i] = this.random.nextInt(); } return a; } } @Test public void testIntStream() { final long seed = System.currentTimeMillis(); assertEquals( new ArrayFromRandom( new Random(seed) ).toArray(SIZE), new Random(seed).ints().limit(SIZE).toArray() ); }
  • 22.
    /24@yegor256 22 @Test public voidtestIntStream() { assertEquals( new ArrayFromRandom( new Random(System.currentTimeMillis() as seed) ).toArray(SIZE), new Random(seed).ints().limit(SIZE).toArray() ); }
  • 23.
  • 24.
    /24@yegor256 24 new Pipe( newTextAsInput(“Hello, world!”), new FileAsOutput(new File(“test.txt”)) ).push();
  • 25.
    /24@yegor256 25 @Test public voidcanCopyTextToByteArray() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); new Pipe( new TextAsInput(“Hello, world!”), new OutputStreamAsOutput(baos) ).push(); assertThat( new String(baos.toByteArray()), Matchers.containsString(“Hello”) ); }
  • 26.
    /24@yegor256 26 Input input= new TeeInput( new TextAsInput(“Hello, world!”), new FileAsOutput(new File(“test.txt”)) ); input.read(); new LengthOfInput(input).asValue(); 6168af9
  • 27.
    /24@yegor256 27 @Test public voidcanCopyTextToByteArray() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); assertThat( new InputAsText( new TeeInput( new TextAsInput(“Hello, world!”), new OutputStreamAsOutput(baos) ) ), Matchers.allOf( equalTo(new String(baos.toByteArray())), containsString(“Hello”) ) ); }
  • 28.
    /24@yegor256 28 @Test public voidcanCopyTextToByteArray() { ByteArrayOutputStream baos; assertThat( new InputAsText( new TeeInput( new TextAsInput(“Hello, world!”), new OutputStreamAsOutput(baos = new ByteArrayOutputStream()) ) ), Matchers.allOf( equalTo(new String(baos.toByteArray())), containsString(“Hello”) ) ); }
  • 29.
    /24@yegor256 29 @Test public voidcanCopyTextToByteArray() { assertThat( new InputAsText( new TeeInput( new TextAsInput(“Hello, world!”), new OutputStreamAsOutput( new ByteArrayOutputStream() as baos ) ) ), Matchers.allOf( equalTo(new String(baos.toByteArray())), containsString(“Hello”) ) ); }
  • 30.
  • 31.