Skip to content

Commit c1e1967

Browse files
committed
Add Dependency Inversion Principle (DIP) example (3/3)
* Add the the third stage of our code. Now the `UserSearcher` will receive an instance of the new `UsersRepository` interface. Benefits explained in the test code.
1 parent 8d977e3 commit c1e1967

File tree

6 files changed

+126
-0
lines changed

6 files changed

+126
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package tv.codely.solid_principles.dependency_inversion_principle.stage_3_dependency_inversion;
2+
3+
import tv.codely.solid_principles.dependency_inversion_principle.User;
4+
5+
import java.util.Optional;
6+
7+
final class UserSearcher {
8+
private UsersRepository usersRepository;
9+
10+
public UserSearcher(UsersRepository usersRepository) {
11+
this.usersRepository = usersRepository;
12+
}
13+
14+
public Optional<User> search(Integer id) {
15+
return usersRepository.search(id);
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package tv.codely.solid_principles.dependency_inversion_principle.stage_3_dependency_inversion;
2+
3+
import tv.codely.solid_principles.dependency_inversion_principle.User;
4+
5+
import java.util.Optional;
6+
7+
public interface UsersRepository {
8+
Optional<User> search(Integer id);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package tv.codely.solid_principles.dependency_inversion_principle.stage_3_dependency_inversion;
2+
3+
import tv.codely.solid_principles.dependency_inversion_principle.User;
4+
5+
import java.util.Collections;
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
import java.util.Optional;
9+
10+
// ℹ️ User repository for test usage. It is used as a _Test double_, more specifically, it's a _Stub_.
11+
// More info on the difference between Stubs and Mocks: https://martinfowler.com/articles/mocksArentStubs.html
12+
final class CodelyTvStaffUsersRepository implements UsersRepository {
13+
private Map<Integer, User> users = Collections.unmodifiableMap(new HashMap<Integer, User>() {
14+
{
15+
put(UserStub.RAFA_ID, UserStub.rafa());
16+
put(UserStub.JAVI_ID, UserStub.javi());
17+
}
18+
});
19+
20+
@Override
21+
public Optional<User> search(Integer id) {
22+
return Optional.ofNullable(users.get(id));
23+
}
24+
}
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package tv.codely.solid_principles.dependency_inversion_principle.stage_3_dependency_inversion;
2+
3+
import tv.codely.solid_principles.dependency_inversion_principle.User;
4+
5+
import java.util.Optional;
6+
7+
// ℹ️ User repository for test usage. It is used as a _Test double_, more specifically, it's a _Fake_.
8+
// More info on the main types of Test doubles: https://testing.googleblog.com/2013/07/testing-on-toilet-know-your-test-doubles.html
9+
final class EmptyUsersRepository implements UsersRepository {
10+
@Override
11+
public Optional<User> search(Integer id) {
12+
return Optional.empty();
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package tv.codely.solid_principles.dependency_inversion_principle.stage_3_dependency_inversion;
2+
3+
import org.junit.jupiter.api.Test;
4+
import tv.codely.solid_principles.dependency_inversion_principle.User;
5+
6+
import java.util.Optional;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
10+
final class UserSearcherShould {
11+
@Test
12+
void find_existing_users() {
13+
// Now we're injecting to the UserSearcher use case different implementation of the new UserRepository interface.
14+
// 👍 Win: We can replace the actual implementation of the UsersRepository used by the UserSearcher.
15+
// That is, we'll not have to modify a single line of the UserSearcher class despite of changing our whole infrastructure.
16+
// This is a big win in terms of being more tolerant to changes.
17+
// 👍 Win: It also make it easier for us to test the UserSearcher without using the actual implementation of the repository used in production.
18+
// This is another big win because this way we can have test such as the following ones which doesn't actually go to the database in order to retrieve the system users.
19+
// This has a huge impact in terms of the time to wait until all of our test suite is being executed (quicker feedback loop for developers 💪).
20+
// 👍 Win: We can reuse the test environment repository using test doubles. See CodelyTvStaffUsersRepository for its particularities
21+
UsersRepository codelyTvStaffUsersRepository = new CodelyTvStaffUsersRepository();
22+
UserSearcher userSearcher = new UserSearcher(codelyTvStaffUsersRepository);
23+
24+
Optional<User> expectedUser = Optional.of(UserStub.rafa());
25+
26+
assertEquals(expectedUser, userSearcher.search(UserStub.RAFA_ID));
27+
}
28+
29+
@Test
30+
void not_find_non_existing_users() {
31+
// 👍 Win: Our test are far more readable because they doesn't have to deal with the internal implementation of the UserRepository.
32+
// The test is 100% focused on orchestrating the Arrange/Act/Assert or Given/When/Then flow.
33+
// More info: http://wiki.c2.com/?ArrangeActAssert and https://www.martinfowler.com/bliki/GivenWhenThen.html
34+
UsersRepository emptyUsersRepository = new EmptyUsersRepository();
35+
UserSearcher userSearcher = new UserSearcher(emptyUsersRepository);
36+
37+
Integer nonExistingUserId = 1;
38+
Optional<User> expectedEmptyResult = Optional.empty();
39+
40+
assertEquals(expectedEmptyResult, userSearcher.search(nonExistingUserId));
41+
}
42+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package tv.codely.solid_principles.dependency_inversion_principle.stage_3_dependency_inversion;
2+
3+
import tv.codely.solid_principles.dependency_inversion_principle.User;
4+
5+
final class UserStub {
6+
static final int RAFA_ID = 1;
7+
static final int JAVI_ID = 2;
8+
9+
private static final String RAFA_NAME = "Rafa";
10+
private static final String JAVI_NAME = "Javi";
11+
12+
static User rafa() {
13+
return new User(RAFA_ID, RAFA_NAME);
14+
}
15+
16+
static User javi() {
17+
return new User(JAVI_ID, JAVI_NAME);
18+
}
19+
}

0 commit comments

Comments
 (0)