Rafael Gutiérrez, Software Engineer @ Encora
Encora Hacker House, May 2022
Effective Programmer's Tests with Java
•Should a Programmer tests?
•Types of Sofware Testing
•Value of having tests
•Java Testing Tools
•Concepts and Patterns
•Test Readability
•Test Maintainability
•Test Trustworthiness
•EOF
Agenda
Should a Programmer test?
•Testing is not only responsibility of QA
•Testing is not something that somebody else does
•We (programmers) must take responsibility
•We (programmers) must write tests for our code to make sure code is
working properly
•We (programmers) must write automated tests
Value of having tests
•Tests helps us catch mistakes
•Tests helps us shape the design
•When applying Test Driven Development (TDD)
•When refactoring
•Tests helps us avoid gold-platingby being explicit about the actual
behavior
Assertions
How do you know your tests worked correctly?
Write code that automate your judgmentabout
whether the code worked
Test Doubles
Generic term for any case where you replace a production object for
testing purposes.
Test Doubles
•Dummy –passed around, not really used. To fill parameter list
•Fake –working implementations but taking shortcuts. For example,
InMemoryTestDatabase
•Stubs –provide canned answers for the test, no responding to
everything outside the test
•Spies –Stubs to record information to be verified later (in asserts)
•Mocks –pre-programmed with expectations. They can throw
exceptions if a call was not expected, or the number of expected calls
is different
AAA Pattern
The AAA Pattern (3A Pattern) suggest to split each tests into three parts:
•Arrange
•Act
•Assert
Test Fixture
Code that is run to configure the system under test
Test data that needs to be set up as a precondition before a test is run—
often this means populating the database with some information,
creating the objects the tests is going to use, etc.
Test Readability
Primitive assertions
•Assertions should express an assumption or intent
•Primitiveassertions hidden the intent behind some meaningless
words or numbers
•Cognitive load for other developers
Primitive assertions -:( example
Primitive assertions -:D example
Primitive assertions –example 2
Or the famous case of "what the heck is this supposed to do?"
Hyperassertions
•Too scrupulous that become brittle and hides intent
•Overwhelming! Checking too many things
•Fragile, breaking frequently (flaky)
•Single Responsibility violation
A test should have only one reason to fail
Hyperassertions-example
Incidental Details
•Code is readable when it's intent, purpose, and meaning is clear
•Scan the code and say "Ah! This thing is doing …"
•Incidental =accompanying but not a major part of something
Incidental Details -:( example
Incidental Details -:D example
Split personality
•Atest should only check one thing and check it well
•If tests doing too many checks –split the test
Split personality -:( example
Split personality -:D example
Split logic
•Nobody likes long methods
•Use "chunking" to improve readability
•cognitive psychology
•Do not blindly extract smaller methods,
but rather do it mindfully
Split logic -:( example
Split logic -:D example
Magic Numbers
•Magic numbers are bad and should be avoided
•Magic numbers appear in assignments, method calls, and other
program statements
•They don’t reveal their meaning
Magic Numbers -:( example
Magic Numbers -:D example 1
Magic Numbers -:D example 2
Setup sermon
•Sometimes we have lot of code just to prepare everything for the
test (setup method)
•The same treatment that we apply to production code should be
applied to test code (at all levels –test methods, setup methods,
etc.)
Setup sermon -
:( example
Setup sermon -:D example
Overprotective tests
•NullPointerException(our old friend) have made Devs to be
overprotective in their code
•Bepragmatic, remove redundant asserts
Conditional Logic
•Loops and conditional statements can be essential tools in test
helpers
•Avoid conditional logic in test methods, they can be
verydistracting
Conditional Logic -:( example
Conditional Logic -:D example
Flaky Tests
•Tests are like our friends
•If some issue was introduced, we should trust our tests to alert us
about that
•Testing when non-deterministic behavior (time, random, etc.) is
involved can be tricky
Flaky Tests -:( example
Flaky Tests -:D example
Crippling file path
•"It runs in my machine!"
•Prevents your tests from running in anyone else machine
•Avoid hardcoded values
Crippling file path -:( example
Crippling file path -:D example
Persistent temp files
•Temporary file should be temporary
•Delete resources your tests create
Persistent temp files -:( example
Persistent temp files -:D example
Sleeping snail
•We should be able to get feedback quickly from our tests
•UsingI/O (Files) in our tests can make them slow
•Using Thread.sleep() will make them slower!
•Just as testing non-deterministic behavior can be tricky, same when
testing Threads
Sleeping snail -:( example
Sleeping snail -:D example
Parameterized mess
•You find yourself with duplicated methods where the test data and
the expected results is the only thing that vary?
•Remove duplication, use parameterized tests
•But use parameterized tests with care
Parameterized mess -:( example
Parameterized mess -:D example
Lack of cohesion in methods
•Simply put, cohesion means that a class represents a single thing, a
single abstraction
•Perfect cohesion means that every field of a class is used in every
method
•In tests classes, this means that every test method is using the
same test fixture
Lack of cohesion in methods -:( example
Lack of cohesion in methods -:D example
Test Trustworthiness
Commented-out tests
Misleading comments
•Comments that suggest something that is not true
•Comments are not executable (like code), they can go stale
Misleading comments -:( example
Misleading comments -:( example
Never-failing tests
•Never-failing tests are like Chuck Norris, they never fail!
•False sense of security
Conditional tests
•Hiding secret conditionals
•Using conditionals for "assumptions" or preconditions for our tests
Conditional tests -:( example
Conditional tests -:D example
•Avoid complex private methods
•Avoid final methods
•Avoid static methods
•Use `new` with care
•Avoid logic in constructors
•Avoid singleton
•Favor composite over inheritance
•Wrap external libraries
•Avoid service lookups
•Favor dependency injection
Simple guidelines for
testable design
Want to learn more?
•Test Infected: Programmers love writing tests, Kent Beck and
ErichGamma (Article)
•Effective Unit Testing, Lasse Koskela, 2013 (Book)
•Growing Object-Oriented Software Guided by Tests,Steve Freeman
and Nat Pryce(Book)
•Test-Driven Development by Example, Kent Beck (Book)
•Unit Testing Principles, Practices and Patterns,Vladimir
Khorikov2020 (Book)