3 ############################
4 Component Tests (with Guice)
5 ############################
7 *Note: This document is a work in progress.*
12 Code which uses `dependency injection with standard
13 annotations <https://wiki-archive.opendaylight.org/view/BestPractices/DI_Guidelines>`__
14 is well suite for component tests.
16 Component tests cover the functionality of 1 single ODL bundle (e.g.
17 netvirt's aclservice, genius' interfacemanager, etc.) They are focused
18 on end-to-end testing of APIs, not individual internal implementation
19 classes (that's what Unit Tests are for). They focus on testing the code
20 in their own module, and typically stub required external services. They
21 assert outcome on the system, often those external services, and the
22 data store. They wire together the internal beans through a Dependency
23 Injection (DI) Framework called Guice, which leverages standard Java
24 annotations in the code, which is equally used by Blueprints.
26 This `presentation from the ODL Summit
27 2016 <https://docs.google.com/presentation/d/1bnwj8CrFGo5KekONYSeIHySdkoXZiewJxkHcZjXnzkQ/edit#slide=id.g17d8ae4d92_0_137>`__
28 has some content related to this topic.
33 In order to use Google Guice in your end2end API component tests to wire
34 bean objects using the same annotations as BP, use:
39 <groupId>org.opendaylight.infrautils</groupId>
40 <artifactId>inject.guice.testutils</artifactId>
41 <version>${infrautils.version}</version>
51 You can write Guice object binding wiring classes like this:
55 public class AclServiceModule extends AbstractGuiceJsr250Module {
57 protected void configureBindings() {
58 bind(AclServiceManager.class).to(AclServiceManagerImpl.class);
59 bind(AclInterfaceStateListener.class);
64 for any OSGi service external to the bundle (not local bean) you use
69 bind(DataTreeEventCallbackRegistrar).annotatedWith(OsgiService.class).to(DataTreeEventCallbackRegistrarImpl.class)
74 Use \*Module classes which define Object Wiring Binding in a JUnit \* Test class
79 public @Rule MethodRule guice = new GuiceRule(AclServiceModule.class, AclServiceTestModule.class);
81 @Inject DataBroker dataBroker;
82 @Inject AclServiceManager mdsalApiManager;
83 @Inject AclInterfaceStateListener mdsalApiManager;
88 In these Component Tests (more so than in simple Unit Tests), one often
89 hits problems due to the extensive use of highly asynchronous code in
90 ODL. Some progress has been made with testing utilities for each
91 respective async API, detailed in this chapter.
93 genius AsyncClusteredDataTreeChangeListenerBase & AsyncDataTreeChangeListenerBase
94 ----------------------------------------------------------------------------------
96 In order to make a test wait for something which happens in a
97 AsyncClusteredDataTreeChangeListenerBase or
98 AsyncDataTreeChangeListenerBase subclass before then asserting on the
99 outcome of what happened, you just add the
100 TestableDataTreeChangeListenerModule to the GuiceRule of your \*Test,
101 and then @Inject AsyncEventsWaiter asyncEventsWaiter, and use
102 awaitEventsConsumption() AFTER having done action like a data store
103 write for which a listener should kick in, and BEFORE reading the
104 datastore to check the effect:
108 public final @Rule MethodRule guice = new GuiceRule(
109 new YourTestModule(),
110 new TestableDataTreeChangeListenerModule());
111 @Inject AsyncEventsWaiter asyncEventsWaiter;
112 asyncEventsWaiter.awaitEventsConsumption();
114 If a AsyncClusteredDataTreeChangeListenerBase or
115 AsyncDataTreeChangeListenerBase (subclass) has "fired", then the
116 AsyncEventsWaiter verifies that a test has indeed used
117 awaitEventsConsumption() - and fails the test with
118 IllegalStateException: Test forgot an awaitEventsConsumption() if it
119 does not. This mechanism ensures that a test does not "forget" to
120 awaitEventsConsumption and assert an expected outcome. NB however that
121 if the test runs fast, it may end before the listeners kicked in, and
122 the IllegalStateException may not always been seen (i.e. leading to a
123 "heisenbug", found with the RunUntilFailureRule). Therefore, if in your
124 test you do not need to awaitEventsConsumption() at all, then you should
125 not use the TestableDataTreeChangeListenerModule. However, this is
126 likely an indication of lack of better test coverage in your test - you
127 probably do want to assert on the effect of your
128 AsyncClusteredDataTreeChangeListenerBase or
129 AsyncDataTreeChangeListenerBase subclasses?
131 infrautils JobCoordinator (formerly genius DataStoreJobCoordinator)
132 ---------------------------------------------------------------------
134 similarly to above, using the JobCoordinatorEventsWaiter:
138 @Inject JobCoordinatorEventsWaiter coordinatorEventsWaiter;
139 coordinatorEventsWaiter.awaitEventsConsumption();
140 (TODO still need to be ported from genius to infrautils)
141 (TODO need to write a combined AsyncEventsWaiter instead of doing e.g. InterfaceManagerTestUtil's waitTillOperationCompletes)
143 It is HIGHLY (!) recommended to FIRST switch code from the @Deprecated
144 DataStoreJobCoordinator (in genius) to the JobCoordinator (in
145 infrautils), because that does not suffer from the problem where a
146 background job can "continue on" from one @Test method into another
147 @Test, or even from one \*Test class into another, due to use of
148 "static", which can lead to VERY confusing log messages.
150 genius ResourceBatchingManager
151 -------------------------------
153 The ResourceBatchingManager API does not yet have an AsyncEventsWaiter
159 Some of our "new style" Component Tests, such as e.g.
160 InterfaceManagerConfigurationTest, and others, still need Thread.sleep()
161 in some places.. the eventual goal is to be able to eventually
162 completely eliminate them from all tests.
167 Let's imagine you want to make a change e.g. in aclservice, just as an
168 example. Specifically, you've added a new argument for another new
169 internal bean or external service to the @Singleton
170 AclServiceManagerImpl @Inject annotated constructor, let's say to an
171 IdManagerService for the sake of this example discussion.
173 A component test based on Guice wiring, such as AclServiceTest, will now
174 fail on you with a message saying something like this:
176 *No implementation for (...your new service...) was bound while locating
177 (...) for the X-th parameter of AclServiceManagerImpl.*
179 The \*Module classes referenced from the GuiceRule in a \*Test is where
180 the wiring is defined - that's what determines, for that test, what
181 implementation class is bound to what service interface etc. If you have
182 a look at e.g. the AclServiceModule & AclServiceTestModule, it should be
183 obvious what that does - just 1 single line for each binding.
185 The error message shown above simply means that an interface was
186 encountered but you have not specified what implementation you would
187 like to use for that interface in a given test. (Different tests could
188 have different Module with varying bindings; but don't have to.)
190 To fix this after having made your change, you would now have to add 1
191 line in AclServiceTestModule to do a bind() of IdManagerService to...
194 If IdManagerServiceas was some new internal helper class of aclservice
195 which you would like to test, then you would just do:
199 bind(IdManagerService.class).to(YOURIdManagerServiceImpl.class);
201 The YOURIdManagerServiceImpl would have a @Singleton annotation on its
202 class, and have an @Inject annotation on its constructor, to
203 automatically get its dependencies injected (and perhaps have
204 @PostConstruct and @PreDestroy, if it has a "lifecycle"; or extend
205 AbstractLifecycle). This is further documented on the `DI
206 Guidelines <https://wiki-archive.opendaylight.org/view/BestPractices/DI_Guidelines>`__
209 Now, in the case of an existing ODL service from another project, you
210 typically didn't actually write your own implementation of the
211 IdManagerService interface. At full system runtime, you probably would
212 like that to use the IdManager class (and you probably added that to
213 your BP XML). So, having understood above, you COULD now be tempted to
214 add this in AclServiceTestModule:
218 bind(IdManagerService.class).to(IdManager.class);
220 but there is two problems with this, 1. small practical (easy to fix),
221 2. conceptual (more important):
223 1. IdManager at the time of initially writing this documentation didnt't
224 have @Singleton @Inject and @PreDestroy on its close() .. this may have
225 already changed - or you could, easily, make a contribution to Genius to
226 change that; I'd recommend making IdManager extend AbstractLifecycle in
227 this case. This can theoretically, even though we wouldn't recommend
228 that, also be worked-around by doing the IdManager "wiring" manually
229 through 2 lines of like new IdManager(...) and then use
230 bind(IdManagerService.class) .toInstance(myIdManager). BUT...
232 2. ... it would, typically IMHO, be wrong to use IdManager as
233 IdManagerService implementation in AclServiceTest. This is more of a
234 general recommendation than a hard rule. The idea is that the component
235 test of aclservice should NOT have to depend on the real implementation
236 of all external services the aclservice code depends on (only all of the
237 internal beans of aclservice). So it would, generally, be considered
238 better to bind a local test implementation of IdManager, which does the
239 minimum you need for the test. A full coverage test of IdManager would
240 be the responsibility of genius' idmanager-impl, not aclservice-impl. So
241 what I would probably start with doing in your case, unless there is a
242 very strong need that you must absolutely have the "full" IdManager for
243 the AclServiceTest, is to just put this into AclServiceTestModule's
248 // import static org.opendaylight.yangtools.testutils.mockito.MoreAnswers.*;
249 bind(IdManagerService.class).toInstance(Mockito.mock(IdManagerService.class, exception());
251 Doing this will resolve the Guice Exception you have run into below. But
252 whenever some aclservice code now actually calls a method
253 IdManagerService, you'll get an UnstubbedMethodException .. and this is
254 normal - because you just mocked IdManagerService! I would still
255 recommend to start like this, and then go about fixing
256 UnstubbedMethodException as they arise when you run AclServiceTest ...
258 Let's for example say that your new code calls IdManagerServices'
259 allocateIdRange() method somewhere - I don't know if it does, so this is
260 just for Illustration. You could make your mocked IdManagerService do
261 something else than throw a UnstubbedMethodException for
262 allocateIdRange() in two different "styles", this is somewhat dependant
263 on personal preference:
265 A) Write out a partial "fake" implementation of it:
267 Write an inner class right there inside at the end of the
268 AclServiceTestModule.java - just because it's easier to have this
269 together and immediately evident when reading code; unless it becomes
270 very long, in which case you could also move it outside, of course:
274 private abstract static class TestIdManagerService implements IdManagerService {
276 public Future<RpcResult<AllocateIdOutput>> allocateId(AllocateIdInput input) {
277 // TODO do something minimalistic here, just useful for the test, not a general implementation
281 Note that the code in such test service implementations are typically
282 simplistic and trivial, and not "real full fledged". Note also that only
283 methods which the test actually requires are implemented; because it's
284 abstract, we don't have to write anything at all for other methods of
287 You can then change the binding in configure() to be:
291 bind(IdManagerService.class).toInstance(Mockito.mock(TestIdManagerService.class, realOrException())
293 Note the subtle difference with the use of realOrException() instead of
296 This first style is vorburger's personal preference; finding this code
297 clearer to read and understand for anyone than "traditional" Mockito
298 usage, and not minding to have to type a few extra lines (for the
299 class), which the IDE will put for me on Ctrl-Space anyway, than having
300 to understand the Mockito magic. This is particular true when the
301 implemented methods have anything but non-trivial arguments and return
302 types - which is often the case in ODL.
304 B) Write the implementation using traditional Mockito API:
306 Write a method, just for clarify, such as:
310 private IdManagerService idManagerService() {
311 IdManagerService idManagerService = Mockito.mock(IdManagerService.class);
312 Mockito.when(idManagerService.allocateId(...)).thenReturn(...);
314 return idManagerService;
317 and then changing the binding in configure() to be:
321 bind(IdManagerService.class).toInstance(idManagerService());