Short answer
Unit test behavior that you own and that can break, especially logic with branches, rules, edge cases, and important failure paths.
Do not unit test code that merely repeats the framework, language, or library behavior unless you add meaningful logic around it.
Good candidates for unit tests
1. Business rules
If the code encodes a rule, test it.
Examples:
boolean isEligibleForDiscount(Customer customer) {
return customer.isActive()
&& customer.getOrdersCount() >= 5
&& !customer.isBlacklisted();
}
This should be unit tested because it represents application-specific behavior.
2. Conditional logic and branching
Test code with if, switch, polymorphic behavior, strategy selection, validation paths, etc.
if (paymentAmount.compareTo(balance) > 0) {
throw new InsufficientFundsException();
}
You should test:
- amount less than balance
- amount equal to balance
- amount greater than balance
- invalid amount if applicable
3. Edge cases
Unit tests are especially valuable around boundaries.
Examples:
- zero
- negative numbers
- empty collections
null, if allowed- maximum/minimum values
- duplicate values
- time zone/date boundary cases
- rounding and precision cases
- permission boundaries
4. Error handling
If your code is supposed to throw, recover, retry, fallback, or translate errors, test that.
try {
gateway.charge(request);
} catch (GatewayTimeoutException ex) {
throw new PaymentUnavailableException("Payment provider is unavailable", ex);
}
This is worth testing because your application behavior depends on it.
5. Transformations and calculations
Any code that maps, calculates, normalizes, sorts, filters, or aggregates data is usually worth testing.
InvoiceSummary summary = invoiceCalculator.calculate(invoice);
Especially test:
- rounding
- currency precision
- missing values
- multiple item combinations
- tax/discount rules
6. Public behavior of a class/module
Prefer testing the public API of a unit rather than every private method.
Instead of asking:
Should I test this private method?
Ask:
Is the behavior produced by this private method observable through the public method?
If yes, test through the public method.
7. Bugs that have occurred before
When you fix a bug, add a test that fails before the fix and passes after it.
This prevents regressions and documents the expected behavior.
Usually not worth unit testing
1. Simple getters and setters
Do not usually test this:
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
Especially with Lombok-generated accessors.
2. Framework wiring
Avoid unit testing things like:
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
}
You usually do not need a unit test just to verify that Spring injects dependencies. That belongs to integration tests if needed.
3. Repository methods provided by Spring Data JPA
This usually does not need a unit test:
Optional<User> findByEmail(String email);
Spring Data already tests its method parsing behavior. If the query is custom or complex, use a repository/integration test instead.
4. Trivial delegation
This is often not valuable:
public UserDto getUser(Long id) {
return userClient.fetchUser(id);
}
Unless there is meaningful behavior such as validation, fallback, authorization, mapping, caching, or error translation.
5. Third-party library behavior
Do not unit test that Jackson serializes JSON, that BigDecimal adds numbers correctly, or that Spring MVC maps annotations correctly.
Test your configuration or behavior if you customized it.
6. Private implementation details
Avoid tests that know too much about internals.
Bad test focus:
Method
calculateStepTwo()was called exactly once.
Better test focus:
Given these inputs, the invoice total is correct.
A practical decision checklist
Ask these questions:
- Does this code contain business logic?
If yes, test it. -
Could this break in a way users or other systems would notice?
If yes, test it. -
Does it have branches, edge cases, calculations, or state changes?
If yes, test it. -
Would a test give me confidence to refactor it?
If yes, test it. -
Am I only testing a framework, library, getter, setter, or annotation?
If yes, probably do not unit test it. -
Would the test be more complex than the code being tested?
If yes, reconsider. Maybe test at a higher level. -
Is this behavior better verified with an integration or end-to-end test?
If yes, use that instead.
Rule of thumb
Use this rough guide:
| Code type | Unit test? |
|---|---|
| Business rules | Yes |
| Calculations | Yes |
| Validation logic | Yes |
| Mapping with logic | Yes |
| Error handling | Yes |
| Edge cases | Yes |
| Bug fixes | Yes |
| Simple getters/setters | Usually no |
| Lombok-generated methods | Usually no |
| Spring dependency injection | Usually no |
| Basic repository method names | Usually no |
| Framework annotations | Usually no |
| Third-party library behavior | No |
Example from a typical Spring/Jakarta application
Probably not worth unit testing:
@Getter
@Setter
@Entity
public class User {
@Id
private Long id;
private String email;
}
Worth testing:
public class UserRegistrationService {
public User register(String email, String password) {
if (!emailValidator.isValid(email)) {
throw new InvalidEmailException(email);
}
if (password.length() < 12) {
throw new WeakPasswordException();
}
return userRepository.save(new User(email, passwordEncoder.encode(password)));
}
}
Tests should cover:
- valid registration
- invalid email
- weak password
- password is encoded
- duplicate email, if handled
- repository failure, if translated or recovered from
Final guideline
Aim for tests that are:
- behavior-focused
- fast
- clear
- stable
- useful during refactoring
Do not chase 100% coverage blindly. High coverage is nice, but meaningful coverage matters more.
