-
-
Notifications
You must be signed in to change notification settings - Fork 65
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
Support property testing with Micronaut #419
Comments
@Nahuel92 Since no one in the jqwik team is a Micronaut user, would you be willing to take a stab at implementing such a module? Could then be delivered as a jqwik artifact through Maven. |
@Nahuel92 Also: Have you asked the Micronaut team if they want to support jqwik testing? Since they are already supporting a Jupiter plugin (I assume), doing the same for jqwik would be straightforward for them. |
Sure, I will reach out to them to see if they are interested in adding support for Jqwik.
I can give it a try. Do you have some pointers to me on how to start developing that module? (In case the Micronaut team is not willing to support Jqwik out-of-the-box). |
@Nahuel92 Take a look at; https://github.com/jlink/jqwik-spring Supporting Micronaut probably also means supporting Weld? There is a in-the-works plugin made by me that is discussed in #386 that is used in several live test suites. With the lifecycle hooks, extending JQwik is actually very easy to do. While there is still room for improvement, the hook that adds support for CDI (together with support for a This hook also has code to suport stuff like:
... which actually initializes the list and injects 20 instances, each from a separate CDI container - great for tests where you want to run multiple containers. So strip that away and the hook becomes even shorter. |
@Nahuel92 @adam-waldenberg So it would probably make sense to accelerate the creation of a jqwik organization on GitHub. If I only had a bit more time... :-/ |
I just created an organisation for all jqwik-related development: https://github.com/jqwik-team/ @Nahuel92 If you want to start work on micronaut extension I can create a repo and add you as collaborator |
Hi, sorry for the delay. I can give it a try on my spare time. |
Hi there. I started working on the Micronaut extension for Jqwik but I am not quite sure about how to integrate the Micronaut's |
@Nahuel92 Can you point to your repo? Or you can bring it over to a repo within jqwik-team. Then we can all have a look. |
I don't think I have permissions to create repos within jqwik-team but I have created my own repo here: https://github.com/Nahuel92/jqwik-micronaut-extension What I have so far compiles but doesn't work but I will spend some time today to see if I can find where the problem is. |
@Nahuel92 I invited you as a maintainer to https://github.com/jqwik-team/jqwik-micronaut I skimmed very shortly through your repo. What I noticed is that there still seems to be a dependency on Jupiter. I'd hope one can get rid of it, but I haven't dived deep enough yet. Feel free to ask questions if you have any. We could also do an online session if you need more information on the hooks mechanism - it's not really documented well enough. |
@Nahuel92 What you want to do here is look at the code from the Junit extension and then move it into a JQwik hook. I would start by getting rid of any dependencies on Junit/Jupiter. |
I accepted the invitation. Totally, we can have an online session but we need to coordinate the date and time (my timezone is GMT-3, or Buenos Aires time). In the meanwhile, I will do my best to make some progress integrating Micronaut with the Jqwik hooks. Maybe I am misunderstanding a few things here and the approach I'm taking is incorrect, so I will continue reading the code and trying to figure out how things should be connected together. |
|
I have spent some time playing around This one: @JqwikMicronautTest
class JqwikMicronautExtensionTest {
@Inject
EmbeddedApplication<?> application;
@Property(tries = 1)
void testItWorks() {
assertThat(application.isRunning()).isTrue();
}
} Fails because |
If you push the actual code to https://github.com/Nahuel92/jqwik-micronaut-extension I can take a look. Some probable sources of confusion:
|
Ups, my bad. Yesterday I spent some time on this but I forgot to commit and push my changes. |
What jumps into the eye at first glance:
|
That partially worked like a charm. Unfortunately, if I don't call The But at least we made one test to pass. Now we can start building on top of this code. I will see if I can figure out why the P.S.: My latest changes are pushed. |
The problem as I see it is that the jqwik hooks use The jqwik way of tackling this is to use the Here's a patch of how'd I tackle it (I'm currently too lazy to fork the repo for a PR branch):
|
BTW, the approach above use a single instance of JqwikMicronautExtension for the whole test run by using |
Thank you. |
I'd try to stay as close to the Jupiter extension as possible. If the Jupiter extension comes with Mockito support by default I'd do it here as well. This issue may be interesting since it shows a straightforward implementation of a MockitoHook: #261 |
I will take a look at that code but, correct me if I'm wrong, I believe it will help in unit tests rather than tests that require an On the other hand, I am also struggling with dynamic (application) properties injection. This is important since the Micronaut's way of injecting dynamic properties is via implementing an interface ( Apologies for the million questions I keep asking :) P.S. I also added a few more tests. |
The only place in |
The Jupiter Micronaut extension should have the same problem - unless they are enforcing |
That worked, though my first approach was the same that the Micronaut team took in the
I am not 100% sure but, based on the beforeAll method, it seems that there is something like you mentioned happening at some point. On the other hand, I noticed jUnit has only one context interface and a couple of implementing classes. Anyway, I will continue working on that dynamic properties feature to see what I can do. In the future, I guess I will do good by copying the test battery used in the jUnit extension to make sure this one has all, or at least most of, the features that that one has. If you feel that is not good or there is a better approach, by all means please let me know. This is the very first project I contribute to (or try to do so) so any suggestion is more than welcomed. Thanks :) |
On second thought, I don't know why the following lines, that you had in before, don't work for jqwik but do for Jupiter:
At first, it looked like it was about fields, but it actually is about a field's metadata, so it probably determines if there is a mock method for a field's type. First assumption, the code in beforeEach is important.
Well, they have special handling for the case of
No worries, your effort is appreciated. In the end, the code in JqwikMicronautExtension should almost look like |
I just pushed code to support dynamic properties injection. I have concerns regarding the creation of a test instance solely for the sake of getting the properties to inject via its I believe we are getting close to have something functional, so I will spend some time trying to bring tests from the Please feel free to add any suggestion/advice, I really appreciate your feedback on this. Thanks! |
I'm quite certain that the transaction handling problem is due to missing calls to So my suggestion is that you call all intercept hooks with
Have a look at Maybe doing this will also resolve the entity manager issues. |
Thanks, that partialy worked for the I added the interceptors you mentioned, but not sure if I added them in the right place. |
Well, I made most tests to pass but I cheated. I added hacky code to begin/rollback a tx accordingly in the You are absolutely right regarding the need to call the I will revisit the |
I just pushed my latest changes including those two new hooks you mentioned. |
At first glance the new hooks look alright. One might argue if |
What I noticed is that the JUnitExtension is doing a bit more than just calling the intercept methods. @Override
public void interceptTestMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
interceptTest(new TestMethodInvocationContext<Object>() {
TestContext testContext;
@Override
public TestContext getTestContext() {
if (testContext == null) {
testContext = buildContext(extensionContext);
}
return testContext;
}
@Override
public Object proceed() throws Throwable {
return invocation.proceed();
}
});
} Maybe |
There's more code missing in @Override
public void beforeEach(final LifecycleContext context, final Object testInstance,
final AnnotatedElement method, final List<Property> propertyAnnotations) {
injectEnclosingTestInstances(context);
super.beforeEach(context, testInstance, method, propertyAnnotations);
}
private void injectEnclosingTestInstances(LifecycleContext lifecycleContext) {
if (lifecycleContext instanceof PropertyLifecycleContext) {
PropertyLifecycleContext propertyLifecycleContext = (PropertyLifecycleContext) lifecycleContext;
propertyLifecycleContext.testInstances().forEach(applicationContext::inject);
}
} All in all I suggest you look at all relevant lifecycle hooks in Some are not relevant, though. I could identify: |
That helped a lot, thanks a lot for the suggestion. I really missed those methods you mentioned, I guess I'm kind of blind these days :) Now I was able to get rid of the flaky test and I also could remove most of the hacky code except from 3 test classes. The weird thing is, if I remove the hacky code from those 3 remaining test classes, they not only fail but also the test from I will revisit the extension code to see if there is more code missing from the But, for now, I will go through the |
The hooks and their proximity values look good to me. Which code do you consider "hacky"? I could see if I have less hacky ideas for it. |
Basically I passed broken tests by cheating and doing manual transaction control. Please take a look at:
And search for my comments One thing that I am not sure about is if Jqwik supports nested tests (I skimmed the docs and I haven't seen it). I'm mentioning this because I haven't brought the nested tests to this extension. |
https://jqwik.net/docs/current/user-guide.html#grouping-tests |
If you look at https://micronaut-projects.github.io/micronaut-test/latest/guide/#_transaction_semantics, they write that |
I tried adding that dependency but unfortunately it didn't work. Not sure where to keep looking at, but I will compare again the The only thing I am aware of is that for the |
public class AfterMicronautContainer implements AfterContainerHook {
...
@Override
public int afterContainerProximity() {
// Run it before @BeforeContainer and after @AfterContainer
return -20;
}
} This will get rid of the error in |
I traced the behaviour of one of the Jpa tests, first with Jupiter then with jqwik: Jupiter Trace
jqwik Trace
What can be seen is that with Jupiter the BeforeEach- and AfterEach methods in the test class are separated from the test method by Sadly, the trace also shows that there's a bug in jqwik's extension mechanism, because the around property hook won't finish with a return but end with an Same recommendation as before: Make |
You should use version Your Gradle file requires another repository then:
|
Thanks for sharing that :) I upgraded the Jqwik dependency, fixed the hook proximity (which, in turn fixed that test you mentioned) and checked the hooks execution and you were absolutely right but, after fixing the execution order tests keeps failing (I even tried changing the In the same vein, I added a few primitive logs to track the hooks execution (not sure what technique you have used, but I tried using Intellij's CPU profiler and I got tons of method calls that I couldn't filter properly and I am not sure how to use VisualVM when running unit tests). Code was rearranged too, in an attempt to improve legibility/maintainability. On the other hand, I checked again the code and we are doing roughly the same Anyway, I will continue looking into this so see what I can find. |
The order of calls is still not as it is in the JUnit5 extension. If you don't mind, I'll try a pull request to show you what I mean. |
I created a pull request that seems to fix all failing tests: Nahuel92/jqwik-micronaut-extension#1 The decisive problem was that Jupiter's intercept mechanism just works differently than I had thought. |
I think there should be a way to configure if things like transactions will be rolled-back per try or per property. @JqwikMicronautTest(transactionMode = TransactionMode.SINGLE_TRANSACTION)
@DbProperties
class JpaSingleTransactionNoSetupTest {
@Inject
private EntityManager entityManager;
@AfterProperty
void tearDown() {
// check test was rolled back
final CriteriaQuery<Book> query = entityManager.getCriteriaBuilder().createQuery(Book.class);
query.from(Book.class);
assertThat(entityManager.createQuery(query).getResultList()).isEmpty();
}
@Property(tries = 10)
void testPersistOne() {
System.out.println("#### testPersistOne");
final Book book = new Book();
book.setTitle("The Stand");
entityManager.persist(book);
final CriteriaQuery<Book> query = entityManager.getCriteriaBuilder().createQuery(Book.class);
query.from(Book.class);
assertThat(entityManager.createQuery(query).getResultList().size()).isEqualTo(1);
}
} This fails with
in the second try. |
Thank you very much, that worked like a charm. I did a few minor changes and now I am in condition to start addressing your suggestions regarding what you mentioned about rolling-back txs per property or per try. |
I suggest this is the right time to do the downgrade to Java 8 and move it over to https://github.com/jqwik-team/jqwik-micronaut. |
Yes, I will move the extension to the repo it belongs to. I also updated the Unfortunately, tests now fail when run together but they all work when run each in isolation. |
I have been delving into a bit into the code to see why tests fail when I run them together but pass when run individually and I suspect that something might be happening in Jqwik (but I can't confirm it yet). I couldn't test it enough due to a lack of time, but here are my findings on the subject. Using the debugger, I came to this point in Jqwik's code. Notice in the previous screenshot how the app flow goes into that Now, same test that failed before but this time ran in isolation: It includes our Again, not sure yet if this is a Jqwik issue or an issue in the extension itself. I need to keep looking into this to figure out. P.S.: We can close this issue and move the conversation to the extension repo if you prefer so. |
Thanks for diving in. I will have a look. |
It's definitely a bug I introduced in 1.7.4-SNAPSHOT. I'll track it down and tell you when it's solved. Sorry for that. |
If you refresh 1.7.4-SNAPSHOT dependency everything should be fine now. |
Worked like a charm now, thank you. I will go ahead and close this issue since we have the extension created in its own repo. If anything pops up, we can address it there. Thanks a lot for your help, suggestions, effort and time :) |
Closing issue as we have a Micronaut extension for Jqwik here. |
Testing Problem
Hi there!
I would like to use property-based tests with Micronaut (via the @MicronautTest annotation) to create a much more meaningful test suite, but as of now it doesn't seem to be possible.
Suggested Solution
Please consider creating an extension for Micronaut in the same way the Jqwik team created one for Spring in the past.
Discussion
Advantages: More meaningful/interesting tests suite.
Disadvantages: An extension needs to be developed for this to work.
The text was updated successfully, but these errors were encountered: