Catalogue Browser

Showing 57 results

Lazily Evaluate Values

Assertion Refactorings

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

BEFORE
assertEquals(expected, actual, message: slowComputation())
AFTER
message = () -> slowComputation()
assertEquals(expected, actual, message)

Sources

StackOverflow

Consolidate Multiple Assertions into a Fluent Assertion

Assertion Refactorings

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

BEFORE
assertEquals(9, actual.size())
assertTrue(actual.contains(element))
AFTER
assertThat(actual)
  .hasSize(9)
  .contains(expected)

Group Multiple Assertions

Assertion Refactorings

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

BEFORE
assert(a1)
...
assert(an)
AFTER
AssertAll(
  () -> assert(a1)
  ...
  () -> assert(an)
)

Instances

Sources

Literature

Replace Assertion

Assertion Refactorings
Variants
Replace the Not OperatorSplit Conditional ParametersReplace Reserved Words

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

BEFORE
assert(!condition)
assert(actual == expected)
assert(actual == true)
assert(actual == null)
AFTER
assertFalse(condition)
assertEquals(expected, actual)
assertTrue(actual)
assertNull(actual)

Enforce Use of Type Inference

Assertion Refactorings

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

BEFORE
Type variable = new ConcreteType()
AnotherType result = methodCall()
AFTER
var variable = new ConcreteType()
var result = methodCall()

Instances

No instances recorded.

Sources

Kashiwa (2021)

Abstract away SUT's Construction

Fixture Refactorings
Variants
AutoFixtureObject MotherTest Data Builder

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

BEFORE
@Test
void t() {
  SUT sut = new SUT(new ComplexDependency(...))
}
AFTER
@Test
void t() {
  SUT sut = ObjectMother.CreateDefaultSUT()
}

Extract Preconditions

Fixture Refactorings
Variants
Class-level FixtureCustom UtilityGuard AssertionConditional Execution

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

BEFORE
if (C) { stmt }
AFTER
assumePrecondition()
stmt

Reuse code with Fixture / Extract Fixture

Fixture Refactorings

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

BEFORE
@Test
void t1() {
  setUpCode()
  stmt
  tearDownCode()
}
AFTER
@BeforeEach
void setUp() {
  setUpCode()
}

Inline Fixture

Fixture Refactorings

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

BEFORE
@BeforeEach
void setUp() {
  specificSetup()
}
AFTER
@Test
void t1() {
  specificSetup()
  stmt
}

Sources

StackOverflowGitHub

Minimize Fixture

Fixture Refactorings

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

BEFORE
@BeforeEach
void setUp() {
  setup1()
  setup2()
  setup3()
}
AFTER
@BeforeEach
void setUp() {
  setup1()
}
@Test
void t() {
  setup2()
  stmt
}

Sources

StackOverflowGitHub

Override Fixtures

Fixture Refactorings

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

BEFORE
class C1 {
  @BeforeEach
  void setUp() { ... }
}
AFTER
class C2 extends C1 {
  @Override
  @BeforeEach
  void setUp() {
    super.setUp()
    additionalSetup()
  }
}

Inject SUT Dependency

Fixture Refactorings
Variants
Constructor-basedReplace Fixture with DI

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

BEFORE
@BeforeEach
void setUp() {
  var dep = new DependencyImpl()
  sut = new SUT(dep)
}
AFTER
@BeforeEach
void setUp() {
  Dependency dep = mock(Dependency.class)
  SUT sut = new SUT(dep)
}

Replace Class Fixture with Method Fixture

Fixture Refactorings

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

BEFORE
@BeforeAll
void setUp() { }
AFTER
@BeforeEach
void setUp() { }

Replace Method Fixture with Class Fixture

Fixture Refactorings

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

BEFORE
@BeforeEach
void setUp() { }
AFTER
@BeforeAll
void setUp() { }

Split Fixture

Fixture Refactorings

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

BEFORE
@BeforeEach
void setUp() {
  heavySetup()
  lightSetup()
}
AFTER
@BeforeAll
void setUpClass() {
  heavySetup()
}
@BeforeEach
void setUp() {
  lightSetup()
}

Sources

StackOverflowGitHub

Merge Fixture

Fixture Refactorings

Motivation

Merge separate fixture methods to simplify setup logic when separation is unnecessary.

Qualities

  • Changeability
  • Simplicity

Code Demonstration

BEFORE
@BeforeAll void s1() {}
@BeforeEach void s2() {}
AFTER
@BeforeEach void setUp() {
  s1()
  s2()
}

Sources

GitHub

Parameterize Test with Framework Support

Test Method Refactorings
Variants
Extract Common LogicMerge Data ProviderConditional Parameterized TestInheritance and Fixture Overrides

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

BEFORE
@Test
void t1() { stmt(P1) }
@Test
void t2() { stmt(P2) }
AFTER
@ParameterizedTest
@CsvSource({P1, P2})
void t(Param P) {
  stmt(P)
}

Dependency-free Test Parameterization

Test Method Refactorings

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

BEFORE
@Test
void t1() {
  setUp(expr1)
  assert(expected1)
}
@Test
void t2() {
  setUp(expr2)
  assert(expected2)
}
AFTER
void t(expr, expected) {
  setUp(expr)
  assert(expected)
}
@Test
void t1() { t(expr1, exp1) }

Enhance Test Report

Test Method Refactorings
Variants
Customize Error MessageParameterize Test NameRename TestAdd Assertion Explanation

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

BEFORE
assertTrue(condition)
assertNotNull(obj)
AFTER
assertTrue("Condition should hold", condition)
assertNotNull("Object must be initialized", obj)

Integration Test to Unit Test

Test Method Refactorings

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

BEFORE
setupDatabaseConnection()
User user = userService.create(...)
assertTrue(userExistsInDB(...))
AFTER
InMemoryRepo fake = new InMemoryRepo()
UserService service = new UserService(fake)
service.create(...)
assertTrue(fake.contains(...))

Sources

StackOverflow

Replace Duplicate Assert with Repeat Tests

Test Method Refactorings

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

BEFORE
@Test
void t() {
  stmt // 1st rep
  assert()
  stmt // nth rep
  assert()
}
AFTER
@RepeatedTest(m)
void t() {
  stmt
  assert()
}

Instances

No instances recorded.

Sources

Literature

Reuse Test Methods

Test Method Refactorings
Variants
Between Unit and IntegrationAcross Projects

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

BEFORE
@Test
void unitT() {
  Dependency mock = mock(Dep.class)
  ...
}
@Test
void intT() {
  Dependency real = new Dep()
  ...
}
AFTER
abstract class BaseTest {
  abstract Dependency dep()
  @Test
  void t() { ... }
}
class Unit extends BaseTest { ... }
class Int extends BaseTest { ... }

Sources

StackOverflow

Introduce Test Parameter Object

Test Method Refactorings

Motivation

Parameterized tests with many parameters may become hard to read.

Introducing parameter objects encapsulates all parameters into one object.

Qualities

  • Uniformity

Code Demonstration

BEFORE
void t(P1, P2, P3, ... Pn) { }
AFTER
void t(ParamObj P) { }

static Stream data() {
  return Stream.of(Arguments.of(new ParamObj(...)))
}

Sources

StackOverflow

Extract Utility Method

Test Method Refactorings
Variants
Extract ActExtract AssertionExtract Fixture Utility

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

BEFORE
@Test
void t1() {
  stmt
  assertEquals(expected, sut.method())
}
AFTER
void customAssert() {
  assertEquals(expected, sut.method())
}
@Test
void t1() {
  stmt
  customAssert()
}

Categorize Test Method

Test Method Refactorings

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

BEFORE
class C {
  @Test
  void fastTest() {}
  @Test
  void slowTest() {}
}
AFTER
@Tag("Fast")
@Test
void fastTest() {}

@Tag("Slow")
@Test
void slowTest() {}

Extract Utility Class

Test Method Refactorings
Variants
Extract ClassPull-up Method

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

BEFORE
class C1 {
  private void common() {}
}
AFTER
class Utils {
  public void common() {}
}
class C1 {
  @Test void t() { Utils.common() }
}

Split Test Method

Test Method Refactorings

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

BEFORE
@Test
void t() {
  assert(A)
  assert(B)
}
AFTER
@Test
void t1() { assert(A) }

@Test
void t2() { assert(B) }

Merge Test Method

Test Method Refactorings

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

BEFORE
@Test
void t1() { assert(A) }
@Test
void t2() { assert(B) }
AFTER
@Test
void t() {
  assert(A)
  assert(B)
}

Instances

Sources

StackOverflow

Migrate Categories

Migration Refactorings

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

BEFORE
@Test
@Category(FastTests.class)
void t() {}
AFTER
@Tag("fast")
@Test
void t() {}

Migrate Runner

Migration Refactorings

Motivation

Migrating tests with custom runners may require adapting JUnit 4’s Runner API to JUnit 5’s Extension API.

Qualities

  • Reliability

Code Demonstration

BEFORE
@RunWith(SpringJUnit4ClassRunner.class)
class SpringTest {}
AFTER
@ExtendWith(SpringExtension.class)
class SpringTest {}

Migrate Mock

Migration Refactorings
Variants
To MockitoTo Powermock

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

BEFORE
// EasyMock
expect(mock.method()).andReturn(val)
replay(mock)
AFTER
// Mockito
when(mock.method()).thenReturn(val)

Migrate Fixture

Migration Refactorings

Motivation

Updates annotations from JUnit 4 style (@Before, @BeforeClass) to JUnit 5 style (@BeforeEach, @BeforeAll).

Qualities

  • Reusability

Code Demonstration

BEFORE
@Before
void setUp() {}
@BeforeClass
static void setupClass() {}
AFTER
@BeforeEach
void setUp() {}
@BeforeAll
static void setupClass() {}

Migrate Assertion

Migration Refactorings
Variants
JUnit to HamcrestTo AssertJJUnit 4 to 5

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

BEFORE
assertEquals(expected, actual)
AFTER
assertThat(actual).isEqualTo(expected)
// or JUnit 5
assertThrows(E.class, () -> stmt)

Migrate Parameterized Test

Migration Refactorings
Variants
TestNG to JUnit 5JUnit 4 to JUnit 5

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

BEFORE
@Test(dataProvider = "data")
void t(P) {}
AFTER
@ParameterizedTest
@CsvSource({P1, P2})
void t(P) {}

Migrate Expected Exception

Migration Refactorings

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

BEFORE
@Test(expected = E.class)
void t() { stmt }
AFTER
@Test
void t() {
  assertThrows(E.class, () -> stmt)
}

Replace Test Annotation between Class and Method

Migration Refactorings

Motivation

TestNG’s @Test annotation can be used on classes. Migration often involves switching scope.

Qualities

  • Simplicity
  • Uniformity

Code Demonstration

BEFORE
class C {
  @Test public void t1() {}
}
AFTER
@Test
class C {
  public void t1() {}
}

Sources

GitHub

Migrate Test Timeout

Migration Refactorings

Motivation

JUnit 5 provides a separate annotation (@Timeout) rather than a parameter on @Test.

Qualities

  • Changeability
  • Uniformity

Code Demonstration

BEFORE
@Test(timeout = 5000)
void t() {}
AFTER
@Timeout(5)
@Test
void t() {}

Sources

GitHub

Replace Loop

Test Smell Refactorings
Variants
Replace Fixed Loop with Repeated TestsReplace For-each with Parameterized

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

BEFORE
@Test
void t() {
  for(int i=0; i<5; i++) {
    assert()
  }
}
AFTER
@RepeatedTest(5)
void t() {
  assert()
}

Instances

Sources

StackOverflow

Replace Mystery Guest with @TempDir

Test Smell Refactorings

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

BEFORE
@Test
void t() {
  File.createTempFile(...)
}
AFTER
@Test
void t(@TempDir File D) {
  D.createTempFile(...)
}

Instances

Sources

GitHub

Solve Race Condition with Resource Lock

Test Smell Refactorings

Motivation

Running test cases concurrently is challenging when relying on shared resources.

Using @ResourceLock synchronizes resource acquisition.

Qualities

  • Independence

Code Demonstration

BEFORE
@Execution(CONCURRENT)
class C { ... }
AFTER
@Execution(CONCURRENT)
class C {
  @ResourceLock(value=SYS_PROPS)
  void t() {}
}

Sources

StackOverflow

Inline Resource

Test Smell Refactorings

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

BEFORE
res = loadResource("file.txt")
AFTER
value = """
  INLINED_DATA
"""

Instances

No instances recorded.

Sources

Literature

Setup External Resource

Test Smell Refactorings

Motivation

Optimistic assumptions about external resources cause non-deterministic behavior.

Explicitly create and release resources before/after testing.

Qualities

  • Reliability

Code Demonstration

BEFORE
void t() {
  process("data.csv")
}
AFTER
@BeforeEach
void setUp() { create("data.csv") }
@AfterEach
void tearDown() { delete("data.csv") }

Instances

No instances recorded.

Sources

Literature

Make Resource Unique

Test Smell Refactorings

Motivation

Overlapping resource names cause crashes in concurrent runs.

Use unique identifiers (e.g., timestamps) for allocated resources.

Qualities

  • Reliability

Code Demonstration

BEFORE
createResource("shared.txt")
AFTER
name = "res-" + timestamp
createResource(name)

Instances

No instances recorded.

Sources

Literature

Introduce Equality Method

Test Smell Refactorings

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

BEFORE
assertEquals(e1, act.p1)
assertEquals(e2, act.p2)
AFTER
assertEquals(expectedObj, actualObj)

Sources

StackOverflow

Mock Time Input

Mocking Refactorings

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

BEFORE
Thread.sleep(5000)
AFTER
Clock mockClock = Clock.fixed(...)
cache.setClock(mockClock)

Sources

StackOverflow

Replace Mocks with Real Instances

Mocking Refactorings

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

BEFORE
when(mock.method()).thenReturn(val)
sut = new SUT(mock)
AFTER
real = new RealDep()
sut = new SUT(real)

Sources

StackOverflowGitHub

Replace Real Instances with Mocks

Mocking Refactorings

Motivation

Mocks are efficient and isolate components.

Useful to prevent undesired access to databases and external APIs.

Qualities

  • Independence
  • Efficiency
  • Reliability

Code Demonstration

BEFORE
db = new CloudDB()
sut = new SUT(db)
AFTER
mockDb = mock(CloudDB.class)
sut = new SUT(mockDb)

Reuse Mock

Mocking Refactorings

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

BEFORE
mock = mock(Dep.class)
when(mock.m()).thenReturn(v)
AFTER
class FakeDep extends Dep { ... }
dep = new FakeDep()

Sources

StackOverflow

Replace Inheritance by Mocking API

Mocking Refactorings

Motivation

Inheritance requires manually crafting tracking logic.

Mocking APIs provide verification mechanisms out of the box.

Qualities

  • Simplicity

Code Demonstration

BEFORE
class T extends MockBase {
  void t() {
    assertCustomTracking(dep)
  }
}
AFTER
@Mock Dep dep
void t() {
  verify(dep).method()
}

Instances

No instances recorded.

Sources

Literature

Replace Mocking API with Anonymous Subclass

Mocking Refactorings

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

BEFORE
spy(new Dep() { ... })
AFTER
new Dep() { ... }

Instances

Sources

GitHub

Replace Test Double Type

Mocking Refactorings

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

BEFORE
Dependency stub = new Dependency() { ... }
AFTER
when(mock.method()).thenReturn(val)

Instances

Sources

GitHub

Replace Test Double with ObjectMother

Mocking Refactorings

Motivation

Centralize complex mock creation logic.

Object Mother provides specific versions of complex type instances.

Qualities

  • Uniformity
  • Efficiency

Code Demonstration

BEFORE
mock = mock(Dep.class)
when(mock.m()).thenReturn(val)
AFTER
sut = new SUT(
  ObjectMother.createConfiguredDep()
)

Instances

Sources

GitHub

Standardize Test Utilities for Project-Wide Reuse

Test Suite Refactorings
Variants
Generalize Utility ClassExport as FrameworkReuse FixturesReuse Cached ContextReuse Cache DB

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

BEFORE
void t() {
  ProjectUtils.specificHelper(p)
}
AFTER
void t() {
  Framework.generalHelper(p)
}

Custom Runner

Test Suite Refactorings

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

BEFORE
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(...)
AFTER
@RunWith(CustomRunner.class)

Sources

StackOverflowGitHub

Custom Annotation

Test Suite Refactorings

Motivation

Custom annotations tag tests for integration or attach metadata.

Composes multiple annotations (Tag, Timeout, Test) into one.

Qualities

  • Uniformity

Code Demonstration

BEFORE
@Test
@Timeout(5)
@Tag("integration")
void t() {}
AFTER
@IntegrationTest
void t() {}

Sources

StackOverflowGitHub

Restructure Test Suite

Test Suite Refactorings
Variants
Nest ClassesMove MethodDenest Classes

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

BEFORE
class FlatTest {
  @Test void t1() {}
  @Test void t2() {}
}
AFTER
class Outer {
  @Nested class C1 { @Test void t1() {} }
  @Nested class C2 { @Test void t2() {} }
}

Extend Framework

Test Suite Refactorings
Variants
Test ExtensionEnforce Architecture

Motivation

JUnit 5 extension model prioritizes composition.

ArchUnit allows teams to define architectural rules in Java.

Qualities

  • Uniformity
  • Reusability
  • Changeability

Code Demonstration

BEFORE
void t() {
  try { ... } finally { release() }
}
AFTER
@ExtendWith(ResourceExt.class)
void t(Resource r) { ... }

Sources

StackOverflowGitHub

Refactoring Catalogue System © 2026 • Built with SvelteKit