/
DependencyContainerTest.kt
193 lines (153 loc) · 6.57 KB
/
DependencyContainerTest.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import exception.CircularDependencyException
import org.amshove.kluent.invoking
import org.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldBeInstanceOf
import org.amshove.kluent.shouldNotBeEqualTo
import org.amshove.kluent.shouldThrow
import org.amshove.kluent.withMessage
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import type.BarImpl
import type.Chicken
import type.Egg
import type.Foo
import type.FooImpl
import type.KotlinObject
import type.TypeMarkedWithSingletonAnnotation
import type.TypeNotMarkedWithSingletonAnnotation
import type.TypeWithOneInterfaceDependency
import type.TypeWithOneNestedInterfaceDependency
import kotlin.reflect.full.primaryConstructor
@ExtendWith(MockitoExtension::class)
internal class DependencyContainerTest {
private val constructorSelector = mock<ConstructorSelector>()
private val subject = DependencyContainer.getNewInstance(constructorSelector)
@Test
fun `register fails when registering existing type`() {
// given
subject.registerBinding(FooImpl::class, Foo::class)
// when
val register = { subject.registerBinding(FooImpl::class, Foo::class) }
// then
invoking(register) shouldThrow IllegalArgumentException::class withMessage "Binding already exists: type.FooImpl"
}
@Test
fun `resolve binding for registered type with parametrized constructor`() {
// given
subject.registerBinding(TypeWithOneInterfaceDependency::class)
subject.registerBinding(Foo::class, FooImpl::class)
whenever(constructorSelector.invoke(TypeWithOneInterfaceDependency::class)).thenReturn(TypeWithOneInterfaceDependency::class.primaryConstructor)
whenever(constructorSelector.invoke(FooImpl::class)).thenReturn(FooImpl::class.primaryConstructor)
// when
val instance = subject.resolveBinding<TypeWithOneInterfaceDependency>()
// then
instance shouldBeInstanceOf TypeWithOneInterfaceDependency::class
}
@Test
fun `resolve binding for non registered type with parameterless constructor`() {
// given
whenever(constructorSelector.invoke(FooImpl::class)).thenReturn(FooImpl::class.primaryConstructor)
// when
val instance = subject.resolveBinding<FooImpl>()
// then
instance shouldBeInstanceOf FooImpl::class
}
@Test
fun `resolve binding for the registered interface`() {
// given
subject.registerBinding(Foo::class, FooImpl::class)
// when
val instance = subject.resolveBinding<Foo>()
// then
instance shouldBeInstanceOf FooImpl::class
}
@Test
fun `resolve instance for registered nested binding`() {
// given
subject.registerBinding(Foo::class, FooImpl::class)
whenever(constructorSelector.invoke(TypeWithOneNestedInterfaceDependency::class)).thenReturn(TypeWithOneNestedInterfaceDependency::class.primaryConstructor)
whenever(constructorSelector.invoke(TypeWithOneInterfaceDependency::class)).thenReturn(TypeWithOneInterfaceDependency::class.primaryConstructor)
whenever(constructorSelector.invoke(FooImpl::class)).thenReturn(FooImpl::class.primaryConstructor)
// when
val instance= subject.resolveBinding<TypeWithOneNestedInterfaceDependency>()
// then
instance shouldBeInstanceOf TypeWithOneNestedInterfaceDependency::class
}
@Test
fun `resolve binding of Kotlin object`() {
// given
subject.registerBinding(KotlinObject::class)
// when
val instance = subject.resolveBinding<KotlinObject>()
// then
instance shouldBeEqualTo KotlinObject
}
@Test
fun `resolve binding fails to resolve instance for unknown binding`() {
// when
val getInstance = { subject.resolveBinding<TypeWithOneInterfaceDependency>() }
// then
invoking(getInstance) shouldThrow IllegalArgumentException::class withMessage "No binding found for interface: type.Foo"
}
@Test
fun `resolve binding fails to resolve instance for non-registered nested binding`() {
// given
subject.registerBinding(BarImpl::class, Foo::class)
whenever(constructorSelector.invoke(TypeWithOneNestedInterfaceDependency::class)).thenReturn(TypeWithOneNestedInterfaceDependency::class.primaryConstructor)
whenever(constructorSelector.invoke(TypeWithOneInterfaceDependency::class)).thenReturn(TypeWithOneInterfaceDependency::class.primaryConstructor)
// when
val getInstance = { subject.resolveBinding<TypeWithOneNestedInterfaceDependency>() }
// then
invoking(getInstance) shouldThrow IllegalArgumentException::class withMessage "No binding found for interface: type.Foo"
}
@Test
fun `resolve binding fails to resolve interface instance`() {
// given
subject.registerBinding(Foo::class, Foo::class)
// when
val getInstance = { subject.resolveBinding<Foo>() }
// then
invoking(getInstance) shouldThrow IllegalArgumentException::class withMessage "No binding found for interface: type.Foo"
}
@Test
fun `resolve fails to circular dependency`() {
// given
subject.registerBinding(Egg::class)
subject.registerBinding(Chicken::class)
// when
val getInstance = { subject.resolveBinding<Egg>() }
// then
invoking(getInstance) shouldThrow CircularDependencyException::class
}
@Test
fun `resolve the same instances for type marked with Singleton annotation`() {
// when
val instance1 = subject.resolveBinding<TypeMarkedWithSingletonAnnotation>()
val instance2 = subject.resolveBinding<TypeMarkedWithSingletonAnnotation>()
// then
instance1 shouldBeEqualTo instance2
}
@Test
fun `resolve the different instances for type not marked with Singleton annotation`() {
// when
val instance1 = subject.resolveBinding<TypeNotMarkedWithSingletonAnnotation>()
val instance2 = subject.resolveBinding<TypeNotMarkedWithSingletonAnnotation>()
// then
instance1 shouldNotBeEqualTo instance2
}
@Test
fun `verify bindings succeeds for empty bindings`() {
// when
subject.verifyBindings()
}
@Test
fun `verify bindings succeeds for correct binding`() {
// when
subject.registerBinding(Foo::class, FooImpl::class)
//then
subject.verifyBindings()
}
}