Skip to content

Commit 1e81db4

Browse files
committed
test(beans/container): 리팩토링 진행 부에 대한 추가 테스트 코드 작성
- 총 81개의 단위테스트 작성, (beans/instantiation=41, context/lifecycle=40) - 주요 커버리지 - Phase 실행 순서가 올바르게 작동함 - Infrastructure 빈이 애플리케이션 빈보다 먼저 생성됨 - BeanPostProcessor가 적절한 시점에 등록됨 - PostInfrastructureInitializer가 basePackages를 올바르게 전달받음 - ContextInitializer가 모든 빈 생성 이후에 실행됨 - postProcessListInjections()이 적절한 시점에 호출됨 - 예외 처리 및 엣지 케이스가 커버됨 - 빈 리스트 및 null 상황이 정상적으로 처리됨
1 parent db7abb8 commit 1e81db4

12 files changed

Lines changed: 2555 additions & 0 deletions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package sprout.beans.instantiation;
2+
3+
import sprout.beans.BeanDefinition;
4+
import sprout.context.BeanFactory;
5+
6+
import java.lang.reflect.Parameter;
7+
import java.util.List;
8+
9+
/**
10+
* 단일 빈 의존성을 해결하는 resolver
11+
*/
12+
public class SingleBeanDependencyResolver implements DependencyTypeResolver {
13+
14+
private final BeanFactory beanFactory;
15+
16+
public SingleBeanDependencyResolver(BeanFactory beanFactory) {
17+
this.beanFactory = beanFactory;
18+
}
19+
20+
@Override
21+
public boolean supports(Class<?> type) {
22+
// List 타입이 아닌 모든 타입을 지원
23+
return !List.class.isAssignableFrom(type);
24+
}
25+
26+
@Override
27+
public Object resolve(Class<?> type, Parameter param, BeanDefinition targetDef) {
28+
return beanFactory.getBean(type);
29+
}
30+
}
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
package sprout.beans.instantiation;
2+
3+
import org.junit.jupiter.api.BeforeEach;
4+
import org.junit.jupiter.api.DisplayName;
5+
import org.junit.jupiter.api.Test;
6+
import sprout.beans.BeanCreationMethod;
7+
import sprout.beans.BeanDefinition;
8+
9+
import java.lang.reflect.Parameter;
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
import static org.junit.jupiter.api.Assertions.*;
14+
import static org.mockito.ArgumentMatchers.any;
15+
import static org.mockito.ArgumentMatchers.eq;
16+
import static org.mockito.Mockito.*;
17+
18+
class CompositeDependencyResolverTest {
19+
20+
private CompositeDependencyResolver resolver;
21+
private DependencyTypeResolver mockResolver1;
22+
private DependencyTypeResolver mockResolver2;
23+
24+
static class Service {}
25+
static class Repository {}
26+
27+
@BeforeEach
28+
void setUp() {
29+
mockResolver1 = mock(DependencyTypeResolver.class);
30+
mockResolver2 = mock(DependencyTypeResolver.class);
31+
32+
List<DependencyTypeResolver> resolvers = new ArrayList<>();
33+
resolvers.add(mockResolver1);
34+
resolvers.add(mockResolver2);
35+
36+
resolver = new CompositeDependencyResolver(resolvers);
37+
}
38+
39+
@Test
40+
@DisplayName("첫 번째 resolver가 지원하면 해당 resolver를 사용한다")
41+
void resolve_usesFirstSupportingResolver() {
42+
// given
43+
Class<?>[] types = {Service.class};
44+
Parameter mockParam = mock(Parameter.class);
45+
Parameter[] params = {mockParam};
46+
BeanDefinition targetDef = createMockBeanDefinition("target", Service.class);
47+
48+
Service expectedService = new Service();
49+
50+
when(mockResolver1.supports(Service.class)).thenReturn(true);
51+
when(mockResolver1.resolve(eq(Service.class), eq(mockParam), eq(targetDef))).thenReturn(expectedService);
52+
53+
// when
54+
Object[] result = resolver.resolve(types, params, targetDef);
55+
56+
// then
57+
assertEquals(1, result.length);
58+
assertSame(expectedService, result[0]);
59+
verify(mockResolver1).resolve(eq(Service.class), eq(mockParam), eq(targetDef));
60+
verify(mockResolver2, never()).supports(any());
61+
}
62+
63+
@Test
64+
@DisplayName("첫 번째가 지원하지 않으면 다음 resolver를 시도한다")
65+
void resolve_triesNextResolverIfFirstDoesNotSupport() {
66+
// given
67+
Class<?>[] types = {Service.class};
68+
Parameter mockParam = mock(Parameter.class);
69+
Parameter[] params = {mockParam};
70+
BeanDefinition targetDef = createMockBeanDefinition("target", Service.class);
71+
72+
Service expectedService = new Service();
73+
74+
when(mockResolver1.supports(Service.class)).thenReturn(false);
75+
when(mockResolver2.supports(Service.class)).thenReturn(true);
76+
when(mockResolver2.resolve(eq(Service.class), eq(mockParam), eq(targetDef))).thenReturn(expectedService);
77+
78+
// when
79+
Object[] result = resolver.resolve(types, params, targetDef);
80+
81+
// then
82+
assertEquals(1, result.length);
83+
assertSame(expectedService, result[0]);
84+
verify(mockResolver1).supports(Service.class);
85+
verify(mockResolver2).supports(Service.class);
86+
verify(mockResolver2).resolve(eq(Service.class), eq(mockParam), eq(targetDef));
87+
}
88+
89+
@Test
90+
@DisplayName("여러 타입의 의존성을 순서대로 해결한다")
91+
void resolve_multipleTypes() {
92+
// given
93+
Class<?>[] types = {Service.class, Repository.class};
94+
Parameter mockParam1 = mock(Parameter.class);
95+
Parameter mockParam2 = mock(Parameter.class);
96+
Parameter[] params = {mockParam1, mockParam2};
97+
BeanDefinition targetDef = createMockBeanDefinition("target", Service.class);
98+
99+
Service expectedService = new Service();
100+
Repository expectedRepo = new Repository();
101+
102+
when(mockResolver1.supports(Service.class)).thenReturn(true);
103+
when(mockResolver1.resolve(eq(Service.class), eq(mockParam1), eq(targetDef))).thenReturn(expectedService);
104+
when(mockResolver1.supports(Repository.class)).thenReturn(false);
105+
106+
when(mockResolver2.supports(Repository.class)).thenReturn(true);
107+
when(mockResolver2.resolve(eq(Repository.class), eq(mockParam2), eq(targetDef))).thenReturn(expectedRepo);
108+
109+
// when
110+
Object[] result = resolver.resolve(types, params, targetDef);
111+
112+
// then
113+
assertEquals(2, result.length);
114+
assertSame(expectedService, result[0]);
115+
assertSame(expectedRepo, result[1]);
116+
}
117+
118+
@Test
119+
@DisplayName("지원하는 resolver가 없으면 예외를 발생시킨다")
120+
void resolve_noSupportingResolver_throwsException() {
121+
// given
122+
Class<?>[] types = {Service.class};
123+
Parameter mockParam = mock(Parameter.class);
124+
Parameter[] params = {mockParam};
125+
BeanDefinition targetDef = createMockBeanDefinition("target", Service.class);
126+
127+
when(mockResolver1.supports(Service.class)).thenReturn(false);
128+
when(mockResolver2.supports(Service.class)).thenReturn(false);
129+
130+
// when & then
131+
RuntimeException exception = assertThrows(RuntimeException.class,
132+
() -> resolver.resolve(types, params, targetDef));
133+
134+
assertTrue(exception.getMessage().contains("No DependencyTypeResolver found"));
135+
assertTrue(exception.getMessage().contains(Service.class.getName()));
136+
}
137+
138+
@Test
139+
@DisplayName("빈 배열을 전달하면 빈 배열을 반환한다")
140+
void resolve_emptyArray() {
141+
// given
142+
Class<?>[] types = {};
143+
Parameter[] params = new Parameter[0];
144+
BeanDefinition targetDef = createMockBeanDefinition("target", Service.class);
145+
146+
// when
147+
Object[] result = resolver.resolve(types, params, targetDef);
148+
149+
// then
150+
assertEquals(0, result.length);
151+
verify(mockResolver1, never()).supports(any());
152+
verify(mockResolver2, never()).supports(any());
153+
}
154+
155+
@Test
156+
@DisplayName("resolver 체인의 순서가 유지된다")
157+
void resolve_maintainsResolverOrder() {
158+
// given
159+
Class<?>[] types = {Service.class};
160+
Parameter mockParam = mock(Parameter.class);
161+
Parameter[] params = {mockParam};
162+
BeanDefinition targetDef = createMockBeanDefinition("target", Service.class);
163+
164+
Service service1 = new Service();
165+
Service service2 = new Service();
166+
167+
// 두 resolver 모두 지원하지만 첫 번째가 우선
168+
when(mockResolver1.supports(Service.class)).thenReturn(true);
169+
when(mockResolver1.resolve(eq(Service.class), eq(mockParam), eq(targetDef))).thenReturn(service1);
170+
171+
when(mockResolver2.supports(Service.class)).thenReturn(true);
172+
when(mockResolver2.resolve(eq(Service.class), eq(mockParam), eq(targetDef))).thenReturn(service2);
173+
174+
// when
175+
Object[] result = resolver.resolve(types, params, targetDef);
176+
177+
// then
178+
assertSame(service1, result[0]); // 첫 번째 resolver의 결과 사용
179+
verify(mockResolver1).resolve(eq(Service.class), eq(mockParam), eq(targetDef));
180+
verify(mockResolver2, never()).resolve(any(), any(), any()); // 두 번째는 호출되지 않음
181+
}
182+
183+
// Helper method
184+
private BeanDefinition createMockBeanDefinition(String name, Class<?> type) {
185+
return new BeanDefinition() {
186+
@Override
187+
public String getName() {
188+
return name;
189+
}
190+
191+
@Override
192+
public Class<?> getType() {
193+
return type;
194+
}
195+
196+
@Override
197+
public BeanCreationMethod getCreationMethod() {
198+
return BeanCreationMethod.CONSTRUCTOR;
199+
}
200+
201+
@Override
202+
public java.lang.reflect.Constructor<?> getConstructor() {
203+
return null;
204+
}
205+
206+
@Override
207+
public Class<?>[] getConstructorArgumentTypes() {
208+
return new Class<?>[0];
209+
}
210+
211+
@Override
212+
public java.lang.reflect.Method getFactoryMethod() {
213+
return null;
214+
}
215+
216+
@Override
217+
public String getFactoryBeanName() {
218+
return null;
219+
}
220+
221+
@Override
222+
public Class<?>[] getFactoryMethodArgumentTypes() {
223+
return new Class<?>[0];
224+
}
225+
226+
@Override
227+
public boolean isProxyTarget() {
228+
return false;
229+
}
230+
231+
@Override
232+
public boolean isConfigurationClassProxyNeeded() {
233+
return false;
234+
}
235+
236+
@Override
237+
public boolean isPrimary() {
238+
return false;
239+
}
240+
};
241+
}
242+
}

0 commit comments

Comments
 (0)