Fundamentals 19 min read

Mastering Unit Testing: Design Principles, Tools, and Real‑World Java Examples

This article explains what makes good code, introduces SOLID and other design principles, defines unit testing, outlines its benefits, provides before‑and‑after Java refactoring examples, and reviews essential testing tools like Fast‑tester, JUnit and Mockito along with coverage metrics.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
Mastering Unit Testing: Design Principles, Tools, and Real‑World Java Examples

Good Code and Design Principles

Good code should be readable, testable, and extensible. Achieve this by following design principles such as SOLID (SRP, OCP, LSP, ISP, DIP), DRY, KISS, YAGNI, and LOD, which together promote high cohesion and low coupling.

SRP : a module has a single reason to change.

OCP : modules are open for extension but closed for modification.

LSP : derived types can replace base types without altering behavior.

ISP : clients should not depend on interfaces they do not use.

DIP : high‑level modules depend on abstractions, not concrete implementations.

DRY : avoid duplicate code.

KISS : keep solutions simple.

YAGNI : do not implement features you don’t need.

LOD : minimize dependencies.

Design patterns further decouple creation, structure, and behavior, leading to high cohesion and loose coupling.

Unit Testing Basics

Unit testing (unit testing) is the practice of developers checking the smallest testable parts of software in isolation, such as a function in C, a class in Java, or a UI component.

Why Write Unit Tests?

Improve code correctness by verifying expected logic, results, and exception handling.

Detect design flaws early (poor testability, bad encapsulation, ill‑designed flows).

Increase readability; simple, well‑tested code is easier to understand.

Enable micro‑refactoring while preserving behavior.

Boost developer confidence in the code base.

Speed up development by avoiding repeated container startups.

Support multiple test scenarios and parameter sets.

Practical Code Refactoring Examples

Before refactoring, an OSS upload method accepted a File object, requiring default files and path handling. After refactoring, it accepts an InputStream, eliminating file creation and path issues.

private static void uploadObject2Oss(OSS client, File file, String bucketName, String dirName) throws Exception { /* ... */ }
private void uploadObject2Oss(OSS client, String bucketName, String dirName, InputStream is, String fileName, long fileSize) throws Exception { /* ... */ }

Another example simplifies a method that mixed channel ratios by replacing List<String[]> with List<SourceInfo>, improving readability and testability.

private List<String> getResultList(List<SourceInfo> sourceInfos, int pageSize, String aliuid) { /* ... */ }

Testing Tools

Fast‑tester : A tool that starts the application once and supports hot‑reloading of test code, dramatically reducing test startup time.

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fast-tester</artifactId>
    <version>1.3</version>
    <scope>test</scope>
</dependency>

JUnit : The classic Java unit‑testing framework for writing and running test cases, supporting annotations like @Test, @Before, @After, etc.

@Test
public void testCreateOssFolder() { /* ... */ }

Mockito : A mocking framework that isolates dependencies, allowing verification of interactions and stubbing of return values.

BenefitService benefitService = Mockito.mock(BenefitService.class);
Mockito.when(benefitService.useBenefit(Mockito.any(BenefitUseDTO.class))).thenReturn(alispResult);

Coverage Metrics

Function coverage – every function is called.

Statement coverage – every statement is executed.

Decision coverage – each branch of conditional statements is taken.

Condition coverage – each boolean sub‑condition evaluates to true and false.

Condition/decision coverage – both decision and condition coverage satisfied.

High coverage is valuable, but the goal is to test complex, important, and necessary code rather than chasing 100% coverage for trivial getters/setters.

Final Thoughts

Effective unit testing combines solid design principles, appropriate tooling, and sensible coverage goals. Teams should cultivate a testing culture, balance effort against business pressure, and view testing as a professional habit that enhances code quality and developer confidence.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javacode qualityJUnitdesign principlesMockito
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.