Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject into test case from launcher #3786

Open
oliviercailloux opened this issue Apr 18, 2024 · 11 comments
Open

Inject into test case from launcher #3786

oliviercailloux opened this issue Apr 18, 2024 · 11 comments

Comments

@oliviercailloux
Copy link

I would like to inject a test class’ field from my launcher before executing the test. IIUC, this is currently impossible. This feature request documents the reason I (think that I) need this.

Overview

I develop a software to grade student’s work. The teacher would write a unit test then run my software. My software would download student 1’s work, compile it on the fly, execute the teacher test on the student 1 work, grade it; download student 2’s work, compile it on the fly, execute the teacher test on the student 2 work, grade it; and so on for each students; and then return a list of all grades to the user.

More details

The teacher would ask students to implement an interface that she provides, say, Additioner.
The teacher would write a unit test class that has a field Additioner studentAdditioner and test methods that use that field.
My software compiles student 1’s class, say, MyAdditioner, that implements Additioner, and should inject an instance of that class into the field studentAdditioner so that the teacher unit test will run on the student 1 instance.

Missing

My software is able to use JUnit’s API to launch the teacher test, but is unable to inject the student instance into the test field. Note that such injection must happen in the context that creates the launcher, not in the context of the junit test, as the test is started by my software.

Workaround

As suggested in the linked threads, one could use JNDI or other external means of communicating an instance, but this would introduce unnecessary complexity and probably be non-transparent to my users (i.e., the teacher).

Sorry if I misunderstood the discussions of the linked threads and it is actually possible, in which case I’d be glad to be directed to some way of proceeding.

@marcphilipp
Copy link
Member

You're right in your assessment that it's currently not possible to inject arbitrary objects via the Launcher for injection into test class instances.

As a workaround, your code could instantiate MyAdditioner and store it in a ThreadLocal. A custom, globally registered Jupiter extension (for example, a TestInstancePostProcessor) could then read the ThreadLocal and inject it into a field of the test class. Would that be feasible in your case?

@oliviercailloux
Copy link
Author

As a workaround, your code could instantiate MyAdditioner and store it in a ThreadLocal. A custom, globally registered Jupiter extension (for example, a TestInstancePostProcessor) could then read the ThreadLocal and inject it into a field of the test class. Would that be feasible in your case?

I believe that this would be feasible, thank you for the suggestion. Though it does feel hackish to use this sort of “global variable” mechanism to pass data around (but it is still way better than the JNDI workaround that I was considering). It also creates potential security issues because the student code itself can access the ThreadLocal. So, for example, if in the future I want to also inject a “correct” (teacher provided) instance MyCorrectAdditioner using the same mechanism, thinking must happen to make sure that the student code is not able to change the thread local variables at some point during the grading process. (I am not claiming that this would be impossible to guarantee, just that it does introduce further things to wonder about.) So I would be happy if a cleaner solution would come to exist, where only my correcting code and the teacher unit test have access to the instances that get injected.

@oliviercailloux
Copy link
Author

Unrelatedly: should this feature request really be marked as component: Jupiter? It feels like a limitation of the launcher itself.

@marcphilipp
Copy link
Member

Unrelatedly: should this feature request really be marked as component: Jupiter? It feels like a limitation of the launcher itself.

I was thinking about Jupiter extensions being able to access it but you're correct to point out that it needs a Platform mechanism as well.

@sbrannen
Copy link
Member

My software is able to use JUnit’s API to launch the teacher test, but is unable to inject the student instance into the test field. Note that such injection must happen in the context that creates the launcher, not in the context of the junit test, as the test is started by my software.

Maybe I'm missing something, but I think I'd do the following...

  • Supply the fully qualified class name of the student's implementation as a JUnit Platform configuration parameter.
  • Write a Jupiter Extension that reads the configuration parameter, uses reflection to instantiate the implementation, and injects the instance into a field in the test class.
  • Perform tests on that instance.

Wouldn't that solve your issue?

@oliviercailloux
Copy link
Author

oliviercailloux commented Apr 21, 2024

Wouldn't that solve your issue?

My software downloads the code, compiles it, possibly runs other checks or does other non trivial things (such as deciding which version to compile and test, depending on deadlines and requirements), and it feels unnatural to me to hand control at that point to a Jupiter extension that would then instantiate. The instantiation logic itself is non trivial and I’d rather not have that started as part of a Jupiter extension. In particular, the code that gets compiled on the fly may be in some temporary file space (or even hopefully in memory only in some future version), with the testing sub-process having reduced access to the disk.

To put it otherwise, the proposed approach requires in a sense to invert the flow of control, meaning, let Jupiter start and control a bigger part of the testing process, rather than my software having responsibility for what concerns the student code preparation and instantiation.

Copy link

github-actions bot commented May 6, 2024

If you would like us to be able to process this issue, please provide the requested information. If the information is not provided within the next 3 weeks, we will be unable to proceed and this issue will be closed.

@oliviercailloux
Copy link
Author

oliviercailloux commented May 6, 2024

I believe that I have provided the requested information. Otherwise, please tell me what is missing. Just replying that I haven’t provided the requested information is rude and unhelpful.

@marcphilipp
Copy link
Member

marcphilipp commented May 6, 2024

@PinkFloyded
Copy link

PinkFloyded commented May 30, 2024

Hi, I'm running into this issue as well. I have dependencies in my test that use @Inject. I'm using the platform launcher api to discover and run these tests. But, my DI library(guice) is currently unable to inject deps into them. I'm not sure why. But, if there was a way to programmatically "provide" extensions like the @ExtendsWith, this might be possible.

@marcphilipp
Copy link
Member

@PinkFloyded Have you seen @RegisterExtension for programmatic extension registration?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants