Catalogue Browser
Showing 57 results
Lazily Evaluate Values
Motivation
Lazily evaluated attributes and values are only computed when they are needed.
In tests, some components are good candidates for being lazily evaluated, i.e., SUT dependencies and assertions.
For instance, assertions' message of failure that are expensive to compute may benefit from lazy evaluation.
Qualities
- Efficiency
Code Demonstration
assertEquals(expected, actual, message: slowComputation())message = () -> slowComputation()
assertEquals(expected, actual, message)Sources
Consolidate Multiple Assertions into a Fluent Assertion
Motivation
Test cases with many assertions are known as the assertion roulette test smell.
One way to fix it is replacing all assertions that have the same actual value with one single Fluent Assertion.
Fluent Assertions enables chaining together different condition checks as unary method invocations.
Qualities
- Uniformity
Code Demonstration
assertEquals(9, actual.size())
assertTrue(actual.contains(element))assertThat(actual)
.hasSize(9)
.contains(expected)Instances
Sources
Group Multiple Assertions
Motivation
Test Methods with multiple assertions may have its execution interrupted prematurely.
Grouping multiple assertions with JUnit 5's assertAll assures that they execute and their result are outputted before terminating.
Qualities
- Effectiveness
Code Demonstration
assert(a1)
...
assert(an)AssertAll(
() -> assert(a1)
...
() -> assert(an)
)Instances
Sources
Replace Assertion
Motivation
Having a conditional logic in the assertions using the not (!) operator affect their readability.
Forced conditional expressions into assertTrue/assertFalse methods to verify objects equality should be replaced.
Developers should use special assertions (e.g. assertNull) instead of passing reserved words.
Qualities
- Uniformity
- Effectiveness
- Simplicity
Code Demonstration
assert(!condition)
assert(actual == expected)
assert(actual == true)
assert(actual == null)assertFalse(condition)
assertEquals(expected, actual)
assertTrue(actual)
assertNull(actual)Sources
Enforce Use of Type Inference
Motivation
Change Return Type and Add Parameter refactorings in production APIs are mainly responsible for breaking tests.
JVM applies type inference to detect automatically the datatype, making tests more resilient.
Qualities
- Brittleness (Reduction)
Code Demonstration
Type variable = new ConcreteType()
AnotherType result = methodCall()var variable = new ConcreteType()
var result = methodCall()Instances
No instances recorded.
Sources
Abstract away SUT's Construction
Motivation
Complex test data creation can be alleviated in different ways.
AutoFixture works as a generic Test Data Builder.
ObjectMother encapsulates complex build logic into static methods for reuse.
Qualities
- Changeability
- Uniformity
- Reliability
- Brittleness
- Reusability
Code Demonstration
@Test
void t() {
SUT sut = new SUT(new ComplexDependency(...))
}@Test
void t() {
SUT sut = ObjectMother.CreateDefaultSUT()
}Extract Preconditions
Motivation
Preconditions determine whether it makes sense to continue execution.
Placing preconditions in a class-level fixture is useful for efficiency.
Conditional Execution (JUnit 5) enables test skipping according to programmatic conditions.
Qualities
- Efficiency
- Reliability
- Uniformity
Code Demonstration
if (C) { stmt }assumePrecondition()
stmtSources
Reuse code with Fixture / Extract Fixture
Motivation
Test methods within a single test class may evolve and share many code similarities.
It is advisable to place the duplicated code into a test fixture (setUp or tearDown).
Qualities
- Reliability
- Uniformity
- Reusability
- Changeability
- Simplicity
Code Demonstration
@Test
void t1() {
setUpCode()
stmt
tearDownCode()
}@BeforeEach
void setUp() {
setUpCode()
}Instances
Sources
Inline Fixture
Motivation
As new methods are added and others removed, test fixtures may become obsolete (General Fixture smell).
One may inline the test fixture, introducing code duplication into the few test methods that still depend on it to improve independence.
Qualities
- Changeability
- Independence
Code Demonstration
@BeforeEach
void setUp() {
specificSetup()
}@Test
void t1() {
specificSetup()
stmt
}Instances
Sources
Minimize Fixture
Motivation
Diverging context of test methods leads to General Fixture.
Minimizing fixtures make tests better suitable as documentation and less sensitive to changes.
Qualities
- Independence
Code Demonstration
@BeforeEach
void setUp() {
setup1()
setup2()
setup3()
}@BeforeEach
void setUp() {
setup1()
}
@Test
void t() {
setup2()
stmt
}Sources
Override Fixtures
Motivation
Testers extract subclasses that override test fixtures to reuse tests under different environments.
Note: JUnit 5 requires specific annotations on overriding methods unlike JUnit 4.
Qualities
- Changeability
- Reusability
Code Demonstration
class C1 {
@BeforeEach
void setUp() { ... }
}class C2 extends C1 {
@Override
@BeforeEach
void setUp() {
super.setUp()
additionalSetup()
}
}Instances
Sources
Inject SUT Dependency
Motivation
Complex SUT dependencies are good mocking candidates.
Injecting such dependencies into the SUT constructor often enables evaluation of interaction with mocked dependency.
Qualities
- Reliability
- Uniformity
- Brittleness
Code Demonstration
@BeforeEach
void setUp() {
var dep = new DependencyImpl()
sut = new SUT(dep)
}@BeforeEach
void setUp() {
Dependency dep = mock(Dependency.class)
SUT sut = new SUT(dep)
}Replace Class Fixture with Method Fixture
Motivation
The components defined in a test fixture should not be modified during the test execution unless the test fixture runs once per test method.
Qualities
- Independence
Code Demonstration
@BeforeAll
void setUp() { }@BeforeEach
void setUp() { }Sources
Replace Method Fixture with Class Fixture
Motivation
Sometimes a test fixture does not have to execute once for each test method.
Constraining execution to once for all methods can speed-up test execution.
Qualities
- Efficiency
Code Demonstration
@BeforeEach
void setUp() { }@BeforeAll
void setUp() { }Instances
Sources
Split Fixture
Motivation
Split the time-consuming part of a fixture (which goes into setupClass) from the part that requires execution before each test (which goes into setup).
Qualities
- Efficiency
Code Demonstration
@BeforeEach
void setUp() {
heavySetup()
lightSetup()
}@BeforeAll
void setUpClass() {
heavySetup()
}
@BeforeEach
void setUp() {
lightSetup()
}Sources
Merge Fixture
Motivation
Merge separate fixture methods to simplify setup logic when separation is unnecessary.
Qualities
- Changeability
- Simplicity
Code Demonstration
@BeforeAll void s1() {}
@BeforeEach void s2() {}@BeforeEach void setUp() {
s1()
s2()
}Instances
Sources
Parameterize Test with Framework Support
Motivation
When multiple methods only differ in terms of data input, it is a good candidate for parameterization.
JUnit 5’s and TestNG’s parameterized tests allow data providers to improve cohesion.
Qualities
- Reusability
- Reliability
- Uniformity
Code Demonstration
@Test
void t1() { stmt(P1) }
@Test
void t2() { stmt(P2) }@ParameterizedTest
@CsvSource({P1, P2})
void t(Param P) {
stmt(P)
}Instances
- SO #33080488
- SO #34284527
- SO #3437962
- SO #38812937
- SO #58063106
- SO #69010737
- Commit: HBase
- Commit: AWS SDK
- Commit: Haikunator
- Commit: HAPI FHIR
- Commit: OpenTripPlanner
- Commit: HTSJDK (Merge Data Provider)
- SO #12051087 (Conditional)
- Commit: Camel (Conditional)
- SO #10431090 (Inheritance)
- Commit: Hadoop (Inheritance)
Sources
Dependency-free Test Parameterization
Motivation
When test cases differ in the Arrange phase (setup), developers may choose to parameterize their fixture or utility methods.
Developers leverage method and superclass extractions to achieve the same behaviour.
Qualities
- Reusability
- Uniformity
Code Demonstration
@Test
void t1() {
setUp(expr1)
assert(expected1)
}
@Test
void t2() {
setUp(expr2)
assert(expected2)
}void t(expr, expected) {
setUp(expr)
assert(expected)
}
@Test
void t1() { t(expr1, exp1) }Instances
Sources
Enhance Test Report
Motivation
Good assertions' error messages are useful for debugging.
Test frameworks rely on method names to identify tests; parameterized tests benefit from custom names.
Avoid Assertion Roulette by adding explanations.
Qualities
- Effectiveness
- Uniformity
Code Demonstration
assertTrue(condition)
assertNotNull(obj)assertTrue("Condition should hold", condition)
assertNotNull("Object must be initialized", obj)Instances
Sources
Integration Test to Unit Test
Motivation
Breaking integration tests into unit tests consists in promoting isolation through mocking.
Ensures robust coverage of isolated behaviours while balancing risk mitigation with quick feedback.
Qualities
- Brittleness
- Independence
- Efficiency
Code Demonstration
setupDatabaseConnection()
User user = userService.create(...)
assertTrue(userExistsInDB(...))InMemoryRepo fake = new InMemoryRepo()
UserService service = new UserService(fake)
service.create(...)
assertTrue(fake.contains(...))Instances
Sources
Replace Duplicate Assert with Repeat Tests
Motivation
When a test repeatedly evaluates the same assertion for a fixed number of times, the code can be simplified with @RepeatedTest.
Qualities
- Simplicity
- Changeability
Code Demonstration
@Test
void t() {
stmt // 1st rep
assert()
stmt // nth rep
assert()
}@RepeatedTest(m)
void t() {
stmt
assert()
}Instances
No instances recorded.
Sources
Reuse Test Methods
Motivation
Oftentimes, unit tests simply differ to integration test due to mocked dependencies.
Generalize test code so real instances and mocks can be used interchangeably.
Qualities
- Reusability
- Reliability
- Independence
Code Demonstration
@Test
void unitT() {
Dependency mock = mock(Dep.class)
...
}
@Test
void intT() {
Dependency real = new Dep()
...
}abstract class BaseTest {
abstract Dependency dep()
@Test
void t() { ... }
}
class Unit extends BaseTest { ... }
class Int extends BaseTest { ... }Instances
Sources
Introduce Test Parameter Object
Motivation
Parameterized tests with many parameters may become hard to read.
Introducing parameter objects encapsulates all parameters into one object.
Qualities
- Uniformity
Code Demonstration
void t(P1, P2, P3, ... Pn) { }void t(ParamObj P) { }
static Stream data() {
return Stream.of(Arguments.of(new ParamObj(...)))
}Instances
Sources
Extract Utility Method
Motivation
Standardize a complex MUT invocation.
Combines many assertion invocations into a custom higher-level assertion method.
Encapsulates environment configuration and clean up.
Qualities
- Reliability
- Uniformity
- Changeability
- Reusability
Code Demonstration
@Test
void t1() {
stmt
assertEquals(expected, sut.method())
}void customAssert() {
assertEquals(expected, sut.method())
}
@Test
void t1() {
stmt
customAssert()
}Categorize Test Method
Motivation
Test suites may have test methods with common features (execution time, code size).
Developers may want to execute those groups separately and in a specific order.
Qualities
- Efficiency
Code Demonstration
class C {
@Test
void fastTest() {}
@Test
void slowTest() {}
}@Tag("Fast")
@Test
void fastTest() {}
@Tag("Slow")
@Test
void slowTest() {}Instances
Sources
Extract Utility Class
Motivation
Utility methods can be extracted and collected into a General Utility Class.
Using named Expectations in JMockit allows setting up custom shared mocks in a base class.
Qualities
- Uniformity
- Independence
Code Demonstration
class C1 {
private void common() {}
}class Utils {
public void common() {}
}
class C1 {
@Test void t() { Utils.common() }
}Sources
Split Test Method
Motivation
Test methods may evolve and accumulate multiple responsibilities (Eager Test).
Test methods can be separated in multiple pure test methods to improve the execution trace for debugging.
Qualities
- Uniformity
Code Demonstration
@Test
void t() {
assert(A)
assert(B)
}@Test
void t1() { assert(A) }
@Test
void t2() { assert(B) }Instances
Sources
Merge Test Method
Motivation
Test suites can accumulate redundancies over time.
When such redundancy is scattered throughout multiple test methods, we can eliminate code duplication by merging them.
Qualities
- Changeability
- Efficiency
- Simplicity
Code Demonstration
@Test
void t1() { assert(A) }
@Test
void t2() { assert(B) }@Test
void t() {
assert(A)
assert(B)
}Instances
Sources
Migrate Categories
Motivation
TestNG's Groups, JUnit 4's Category, and JUnit 5's Tags are equivalent features but differ in flexibility.
Tags (JUnit 5) integrate with other features such as custom display name.
Qualities
- Simplicity
- Effectiveness
Code Demonstration
@Test
@Category(FastTests.class)
void t() {}@Tag("fast")
@Test
void t() {}Instances
Sources
Migrate Runner
Motivation
Migrating tests with custom runners may require adapting JUnit 4’s Runner API to JUnit 5’s Extension API.
Qualities
- Reliability
Code Demonstration
@RunWith(SpringJUnit4ClassRunner.class)
class SpringTest {}@ExtendWith(SpringExtension.class)
class SpringTest {}Instances
Sources
Migrate Mock
Motivation
Ease of use and active development are reasons to migrate to Mockito.
Powermock includes support for mocking static methods where earlier frameworks might not.
Qualities
- Changeability
Code Demonstration
// EasyMock
expect(mock.method()).andReturn(val)
replay(mock)// Mockito
when(mock.method()).thenReturn(val)Sources
Migrate Fixture
Motivation
Updates annotations from JUnit 4 style (@Before, @BeforeClass) to JUnit 5 style (@BeforeEach, @BeforeAll).
Qualities
- Reusability
Code Demonstration
@Before
void setUp() {}
@BeforeClass
static void setupClass() {}@BeforeEach
void setUp() {}
@BeforeAll
static void setupClass() {}Instances
Sources
Migrate Assertion
Motivation
Adopted test library may lack desired features (fluent API).
AssertJ is a fluent assertions library compatible with most test frameworks.
JUnit 5 introduces new assertions like assertThrows.
Qualities
- Uniformity
Code Demonstration
assertEquals(expected, actual)assertThat(actual).isEqualTo(expected)
// or JUnit 5
assertThrows(E.class, () -> stmt)Instances
Sources
Migrate Parameterized Test
Motivation
JUnit 5 supports parameterization by default, allows multiple argument providers, and includes over ten provider types (CSV, Method, etc).
Qualities
- Simplicity
- Reusability
Code Demonstration
@Test(dataProvider = "data")
void t(P) {}@ParameterizedTest
@CsvSource({P1, P2})
void t(P) {}Sources
Migrate Expected Exception
Motivation
Using try-catch or @Test(expected=...) enables false positives.
JUnit 5's assertThrows preserves execution of remaining statements and targets specific calls.
Qualities
- Simplicity
Code Demonstration
@Test(expected = E.class)
void t() { stmt }@Test
void t() {
assertThrows(E.class, () -> stmt)
}Instances
Sources
Replace Test Annotation between Class and Method
Motivation
TestNG’s @Test annotation can be used on classes. Migration often involves switching scope.
Qualities
- Simplicity
- Uniformity
Code Demonstration
class C {
@Test public void t1() {}
}@Test
class C {
public void t1() {}
}Instances
Sources
Migrate Test Timeout
Motivation
JUnit 5 provides a separate annotation (@Timeout) rather than a parameter on @Test.
Qualities
- Changeability
- Uniformity
Code Demonstration
@Test(timeout = 5000)
void t() {}@Timeout(5)
@Test
void t() {}Instances
Sources
Replace Loop
Motivation
When a test repeatedly evaluates the same assertion for a fixed number of times, it can be simplified.
Testing permutation of values with nested loops should be parameterized.
Qualities
- Uniformity
- Changeability
Code Demonstration
@Test
void t() {
for(int i=0; i<5; i++) {
assert()
}
}@RepeatedTest(5)
void t() {
assert()
}Instances
Sources
Replace Mystery Guest with @TempDir
Motivation
Using external resources (files) is a symptom of Mystery Guest.
Using @TempDir ensures resources are created and cleaned up automatically.
Qualities
- Independence
Code Demonstration
@Test
void t() {
File.createTempFile(...)
}@Test
void t(@TempDir File D) {
D.createTempFile(...)
}Instances
Sources
Solve Race Condition with Resource Lock
Motivation
Running test cases concurrently is challenging when relying on shared resources.
Using @ResourceLock synchronizes resource acquisition.
Qualities
- Independence
Code Demonstration
@Execution(CONCURRENT)
class C { ... }@Execution(CONCURRENT)
class C {
@ResourceLock(value=SYS_PROPS)
void t() {}
}Instances
Sources
Inline Resource
Motivation
Tests that use external resources are not self-contained.
Incorporate the resource content into the test code to remove dependency.
Qualities
- Uniformity
- Changeability
- Effectiveness
Code Demonstration
res = loadResource("file.txt")value = """
INLINED_DATA
"""Instances
No instances recorded.
Sources
Setup External Resource
Motivation
Optimistic assumptions about external resources cause non-deterministic behavior.
Explicitly create and release resources before/after testing.
Qualities
- Reliability
Code Demonstration
void t() {
process("data.csv")
}@BeforeEach
void setUp() { create("data.csv") }
@AfterEach
void tearDown() { delete("data.csv") }Instances
No instances recorded.
Sources
Make Resource Unique
Motivation
Overlapping resource names cause crashes in concurrent runs.
Use unique identifiers (e.g., timestamps) for allocated resources.
Qualities
- Reliability
Code Demonstration
createResource("shared.txt")name = "res-" + timestamp
createResource(name)Instances
No instances recorded.
Sources
Introduce Equality Method
Motivation
Test methods with multiple assertions on object properties are hard to debug.
Compare objects with an expected object (equals method) rather than properties separately.
Qualities
- Changeability
- Uniformity
- Brittleness
- Simplicity
Code Demonstration
assertEquals(e1, act.p1)
assertEquals(e2, act.p2)assertEquals(expectedObj, actualObj)Instances
Sources
Mock Time Input
Motivation
Testing software relying on clock time is challenging.
Mock the time to speed-up tests and avoid synchronization problems.
Qualities
- Efficiency
- Effectiveness
Code Demonstration
Thread.sleep(5000)Clock mockClock = Clock.fixed(...)
cache.setClock(mockClock)Instances
Sources
Replace Mocks with Real Instances
Motivation
Mocks might behave differently than real objects.
Simpler dependencies may not benefit from mocking.
Replacing mock with real instance can be beneficial if performance is satisfactory.
Qualities
- Uniformity
Code Demonstration
when(mock.method()).thenReturn(val)
sut = new SUT(mock)real = new RealDep()
sut = new SUT(real)Instances
Sources
Replace Real Instances with Mocks
Motivation
Mocks are efficient and isolate components.
Useful to prevent undesired access to databases and external APIs.
Qualities
- Independence
- Efficiency
- Reliability
Code Demonstration
db = new CloudDB()
sut = new SUT(db)mockDb = mock(CloudDB.class)
sut = new SUT(mockDb)Instances
Sources
Reuse Mock
Motivation
When specific features are heavily used, many tests require mocking it.
Extract a Fake or Stub class used across many tests.
Qualities
- Reusability
- Changeability
Code Demonstration
mock = mock(Dep.class)
when(mock.m()).thenReturn(v)class FakeDep extends Dep { ... }
dep = new FakeDep()Instances
Sources
Replace Inheritance by Mocking API
Motivation
Inheritance requires manually crafting tracking logic.
Mocking APIs provide verification mechanisms out of the box.
Qualities
- Simplicity
Code Demonstration
class T extends MockBase {
void t() {
assertCustomTracking(dep)
}
}@Mock Dep dep
void t() {
verify(dep).method()
}Instances
No instances recorded.
Sources
Replace Mocking API with Anonymous Subclass
Motivation
Java's anonymous classes are commonly used for creating stubs.
Wrapping them with Spies can be redundant; manual implementation may be simpler.
Qualities
- Reusability
Code Demonstration
spy(new Dep() { ... })new Dep() { ... }Instances
Sources
Replace Test Double Type
Motivation
Switching between Dummy, Stub, Fake, Spy, and Mock based on needs (flexibility vs simplicity).
E.g., replacing a Stub with a Mock for verification capabilities.
Qualities
- Efficiency
- Simplicity
Code Demonstration
Dependency stub = new Dependency() { ... }when(mock.method()).thenReturn(val)Instances
Sources
Replace Test Double with ObjectMother
Motivation
Centralize complex mock creation logic.
Object Mother provides specific versions of complex type instances.
Qualities
- Uniformity
- Efficiency
Code Demonstration
mock = mock(Dep.class)
when(mock.m()).thenReturn(val)sut = new SUT(
ObjectMother.createConfiguredDep()
)Instances
Sources
Standardize Test Utilities for Project-Wide Reuse
Motivation
Generalize utility methods' return types/parameters to promote reuse.
Export utility classes to share logic among projects.
Reuse cached Spring contexts to avoid initialization overhead.
Qualities
- Reusability
- Efficiency
Code Demonstration
void t() {
ProjectUtils.specificHelper(p)
}void t() {
Framework.generalHelper(p)
}Instances
Sources
Custom Runner
Motivation
Use JUnit 4 rules or subclass existing runners to customize behavior.
Useful when only one runner is allowed per class.
Qualities
- Reusability
- Changeability
Code Demonstration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(...)@RunWith(CustomRunner.class)Sources
Custom Annotation
Motivation
Custom annotations tag tests for integration or attach metadata.
Composes multiple annotations (Tag, Timeout, Test) into one.
Qualities
- Uniformity
Code Demonstration
@Test
@Timeout(5)
@Tag("integration")
void t() {}@IntegrationTest
void t() {}Instances
Sources
Restructure Test Suite
Motivation
Nested test classes organize tests into logical groups with scoped fixtures.
Moving test methods reflects changes in production code structure.
Qualities
- Uniformity
Code Demonstration
class FlatTest {
@Test void t1() {}
@Test void t2() {}
}class Outer {
@Nested class C1 { @Test void t1() {} }
@Nested class C2 { @Test void t2() {} }
}Instances
Sources
Extend Framework
Motivation
JUnit 5 extension model prioritizes composition.
ArchUnit allows teams to define architectural rules in Java.
Qualities
- Uniformity
- Reusability
- Changeability
Code Demonstration
void t() {
try { ... } finally { release() }
}@ExtendWith(ResourceExt.class)
void t(Resource r) { ... }