Unit testing a class calling Guice.createInjector()

54 views
Skip to first unread message

Goyot, Martin

unread,
Jun 29, 2021, 8:50:52 AM6/29/21
to jenkin...@googlegroups.com
Hi there,

I have a question regarding unit testing a plugin which uses Guice. As per this old (?) documentation : https://wiki.jenkins.io/display/JENKINS/Dependency+Injection I implemented the dependency injection in my plugins. The thing is, if we take the example of the documentation, how can we test the `MyPublisher.java` class? Because it is directly calling the `Guice.createInjector` method with configured modules from within the class itself. I can indeed easily test the subsequent classes like `MySvc` but not the `MyPublisher` one.

I think I have two options there, but I don't know if there are maybe others:

1. There is something that I don't know about Guice which can let me substitute the modules loaded into Guice for mocked ones.
2. I'm suppose to rely on the `.inject(this)` mechanism and in my tests I use the `setMySvc` method to circumvent the call to `Guice.createInjector()` which will be made if I don't have a MySvc set.

Is the second option the good one? Or is there some kind of other way of doing this that I'm not aware of?

Best Regards,
Martin

--



Jesse Glick

unread,
Jun 29, 2021, 10:10:33 AM6/29/21
to Jenkins Dev
Guice should never have been added to Jenkins to begin with and I would not recommend using it from a plugin.

Tim Jacomb

unread,
Jun 29, 2021, 11:14:51 AM6/29/21
to Jenkins Developers
The guice integration within Jenkins isn't great, the general consensus from people who have experienced issues with it is don't use it.
And as soon as you hit it an issue with it rip it out if you come across it somewhere 

On Tue, 29 Jun 2021 at 15:10, Jesse Glick <jgl...@cloudbees.com> wrote:
Guice should never have been added to Jenkins to begin with and I would not recommend using it from a plugin.

--
You received this message because you are subscribed to the Google Groups "Jenkins Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jenkinsci-de...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jenkinsci-dev/CANfRfr20Sjbdqq4JEcadn8L9gYyxSOAw%3DkmO7D8Q4_1Ve-cn%3DQ%40mail.gmail.com.

Goyot, Martin

unread,
Jun 30, 2021, 3:31:23 AM6/30/21
to jenkin...@googlegroups.com
Hi Jesse and Tim,

Thanks for the feedback. Let's say I remove my use of Guice, am I left with something else to deal with dependency injection ?  Is there a Jenkins way of dealing with this, a particular pattern advised, or should I rebuild the dependencies myself where needed ? I'm interested in any pointer to deal with this injection.

Thanks in advance,
Martin

--


Ullrich Hafner

unread,
Jun 30, 2021, 3:47:35 AM6/30/21
to JenkinsCI Developers
Which dependencies do you mean? Normally there is no need to use dependency injection in Jenkins at all. Typically all required objects are provided as parameters to the extension point methods. 

Maybe a pointer to the code would be helpful...

Daniel Beck

unread,
Jun 30, 2021, 4:30:50 AM6/30/21
to JenkinsCI Developers
On Wed, Jun 30, 2021 at 9:47 AM Ullrich Hafner <ullrich...@gmail.com> wrote:
Which dependencies do you mean? Normally there is no need to use dependency injection in Jenkins at all. Typically all required objects are provided as parameters to the extension point methods. 

Didn't you write the JenkinsFacade to make that testable without a full instance running? :-)

Goyot, Martin

unread,
Jun 30, 2021, 5:32:19 AM6/30/21
to jenkin...@googlegroups.com
> Maybe a pointer to the code would be helpful...


> Which dependencies do you mean? Normally there is no need to use dependency injection in Jenkins at all. Typically all required objects are provided as parameters to the extension point methods.

I'm not necessarily talking about Jenkins dependencies that are provided by Jenkins itself but dependencies coming from our own code.

--






--
You received this message because you are subscribed to the Google Groups "Jenkins Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jenkinsci-de...@googlegroups.com.

Tim Jacomb

unread,
Jun 30, 2021, 7:21:07 AM6/30/21
to Jenkins Developers
If it's your code normally you would inject yourself via the constructor.

If it's Jenkins dependencies you would get them via 'getContex().get(Computer.class)'

Example PR where I removed Guice recently: https://github.com/jenkinsci/sauce-ondemand-plugin/pull/83/files
Jump to src/main/java/com/saucelabs/jenkins/pipeline/SauceConnectStep.java

Thanks
Tim

Ullrich Hafner

unread,
Jun 30, 2021, 10:40:01 AM6/30/21
to JenkinsCI Developers
Am 30.06.2021 um 13:20 schrieb Tim Jacomb <timja...@gmail.com>:

If it's your code normally you would inject yourself via the constructor.

Or if you are using a class like `Gson` simply create an instance where you need it. It does not provide any benefit as far as I can see if you wrap that in an interface.

Additionally, if you have only one implementation for an interface this might also indicate that the interface is obsolete (see your Impl classes for the corresponding interfaces). You can stub classes with Mockito as well, there is no need to introduce additional interfaces just for the sake of stubbing.

Jesse Glick

unread,
Jun 30, 2021, 11:04:47 AM6/30/21
to Jenkins Dev
On Wed, Jun 30, 2021 at 10:40 AM Ullrich Hafner <ullrich...@gmail.com> wrote:
You can stub classes with Mockito as well

Beware mocking frameworks like Mockito and Powermock. They can cause failures which are terribly hard to debug; and tests can fail when some dependencies are updated even when the update is fully compatible from an API perspective, merely because some method deep in the call stack began calling something new which you did not mock yet. I would advise against their use.

True unit tests should not require any special DI or mocking framework if you arrange code with this purpose in mind. Most Jenkins tests however use `JenkinsRule` to actually run code in a realistic context—takes ~5s to run a test case but gives reasonable confidence that the tested code path actually matches production. Often `@TestExtension` is used to add special extensions active only in a particular test case (note that this is distinct from DI since you are adding extensions to a pool, though it is possible to override a singleton extension called via `ExtensionList.lookupSingleton` using the `ordinal` attribute). Some plugins also use WireMock to let `JenkinsRule`-based tests interact with a mock version of an external service.

Daniel Beck

unread,
Jun 30, 2021, 11:29:34 AM6/30/21
to JenkinsCI Developers
On Wed, Jun 30, 2021 at 5:04 PM Jesse Glick <jgl...@cloudbees.com> wrote:
though it is possible to override a singleton extension called via `ExtensionList.lookupSingleton` using the `ordinal` attribute).

There can be only one (otherwise it throws an exception).

Jesse Glick

unread,
Jun 30, 2021, 11:50:50 AM6/30/21
to Jenkins Dev
On Wed, Jun 30, 2021 at 11:29 AM Daniel Beck <db...@cloudbees.com> wrote:
though it is possible to override a singleton extension called via `ExtensionList.lookupSingleton` using the `ordinal` attribute).

There can be only one (otherwise it throws an exception).

Ah, true. Still, sometimes `ordinal` can be used to override production extensions for purposes of testing. 
Reply all
Reply to author
Forward
0 new messages