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
TRUNK-5817: Migrate web and webapp package tests to Junit 5 #3392
Conversation
@achilep you are trying to mock static methods with mockito. mockito does not allow you to do that. that's why we used powermock. now that we migrate to junit 5 and we cannot use powermock its not emough to just remove powermock. you need to either use reflection to set some fields that will then execute the code in a way you want in the specific test or make it an integration test. I suggest to try reflection first. for example using the spring reflectionutils you already used in the last test you migrated. if you need to get specific information on the code you are testing I suggest reaching out to @WolfSchlegel |
thanks @teleivo |
DatabaseUpdater.getUnrunDatabaseChanges(liquibaseProvider); | ||
PowerMockito.verifyStatic(DatabaseUpdater.class); | ||
verify(DatabaseUpdater.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@teleivo i try to set a changeLogDetective field ReflectionTestUtils.setField(DatabaseUpdater.class, "changeLogDetective", changeLogDetective);
but i am getting this error :
java.lang.IllegalStateException: Could not access method or field: Can not set static final org.openmrs.liquibase.ChangeLogDetective field org.openmrs.util.DatabaseUpdater.changeLogDetective to org.openmrs.liquibase.ChangeLogDetective
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its always a good idea to read the javadocs of the methods you are using. if you are talking about the spring reflectionutils then yes the javadoc says it does not support final fields. for these you have to use powermock.reflection you can find a usage in the api package. I think something like Whitebox.set...
You will then also need to adjust the dependencies of the web package. Also see the api pom.XML where I added a test score dependency for powermock.reflection and excluded the other 2 powermock dependencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @teleivo . i will eventually used whitebox.set. I avoided using it, since we want to remove powermock dependencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you have a look at the api pom? https://github.com/openmrs/openmrs-core/blob/master/api/pom.xml#L24-L45
I did exclude powermock mockito library and its junit 4 runner in the api package. what we are using is simply the powermock reflection library. since that one allows setting of static final fields.
@achilep any luck replacing the |
thanks @teleivo , no luck. this is the last error that i try to solve : |
please push your latest changes and let me know which test is failing |
a8bb03b
to
70e4055
Compare
@PrepareForTest(DatabaseUpdater.class) | ||
@PowerMockIgnore({ "com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "javax.management.*", "org.w3c.dom.*" }) | ||
public class UpdateFilterModelTest { | ||
public class UpdateFilterModelTest extends BaseContextSensitiveTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i had some error with the database connection that is why i extends BaseContextSensitiveTest
assertThat(model.changes, is(empty())); | ||
|
||
PowerMockito.verifyStatic(DatabaseUpdater.class); | ||
DatabaseUpdater.getUnrunDatabaseChanges(liquibaseProvider); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the error is with this method . i have some difficulty to manipulate the behavior of this method .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
java.lang.IllegalStateException: identifying the snapshot version that had been used to initialize the OpenMRS database failed as no candidate change set resulted in zero un-run changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the exception is cause by this line
throw new IllegalStateException( |
i have try to solve the error with this FieldUtils.writeField(changeLogDetective, "changeLogVersionFinder", changeLogVersionFinder, true);
but it did not work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@WolfSchlegel can you help us out here. So with JUnit 5 PowerMock and thus mocking static methods does not work anymore. We are migrating all test to JUnit 5. This might be the last one we have to migrate. What we can do is use reflection to set static fields. Can you help us out with replacing powermock in this test with reflection so we keep its original intent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking into this and apologies for the delay...
org/openmrs/web/filter/update/UpdateFilterModelTest.java
is a unit test and extending BaseContextSensitiveTest
makes it an integration test. I would advise against that and would revert that change first.
In the context of https://issues.openmrs.org/browse/TRUNK-4830 I wrote a few new integration tests and found that tests based on BaseContextSensitiveTest
are not well isolated and understanding the behaviour of individual tests was quite hard. Eventually I created a new base class for integration tests.
@achilep could you please change the test so that it is a unit test again and ping me when this is done?
Thank you,
Wolf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I have a look
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there,
please have a look at WolfSchlegel@6a08797.
I introduced a wrapper for the (mostly static) org/openmrs/util/DatabaseUpdater.java
class so that PowerMock is no longer needed for mocking it. This is good news for org/openmrs/web/filter/update/UpdateFilterModelTest.java
.
However, this comes at a cost as the next question is how to unit test the new wrapper without resorting to PowerMock. I gave JMockit a short try but bumped into issues that are (allegedly) resolved by rearranging the order of dependencies in the pom file (yuck) and it seems a java agent needs to be registered as well for JMockit (yuck-ish).
So I am back at square one looking for a unit test that proves that the all new org/openmrs/util/DatabaseUpdaterWrapper.java
class plays nicely with the incumbent org/openmrs/util/DatabaseUpdater.java
class.
To be frank, I am not sure how Java reflections or respective Spring utilities help here. At the end of the day I need to verify that the wrapper calls the (static) methods. For this purpose I introduced some static helper variables and a helper method in org/openmrs/util/DatabaseUpdater.java
.
Please have a look and let me know what you think. I believe we are facing a trade-off between not testing something and adding test helpers in production code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you @WolfSchlegel !
I think your suggestion for adding a wrapper is good. I added a suggestion in the code at the commit you sent.
Our goal with the UpdateModelFilterTest is to test that the filter makes sure updates are made. Your wrapper enables us to do just that. The update logic should be tested else were.
I am ok with not testing the wrapper as its super thin. This tradeoff is also ok for me since it enables us to remove powermock which hopefully forces us to rethink our designs going forward. The proliferation of static methods with side effects in the code base are a sign of bad design for me that circumvent springs dependency injection and harm testability.
Can you please bring in your changes while keeping the UpdateModelFilterTest a JUnit 4 test. We can chat about my suggestion on the code. @achilep will then do the migration to JUnit 5 once the changes are merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @WolfSchlegel
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your feedback, @teleivo .
I removed the hand-rolled verification from the DatabaseUpdater
class and moved the new wrapper to the same package that contains the UpdateFilterModel
class. Making the wrapper package-private resulted in flaky Mockito behaviour when running mvn clean install
so the wrapper continues to be a public class.
I created this pull request which is based on the current OpenMRS master. If you do not want to get an implicit rebase against master in mid flight, best cherry pick the relevant changes from here.
Please let me know if there is anything else I can do.
hello @WolfSchlegel , i have done . it is now a unit test |
@WolfSchlegel just to reiterate what we try to achieve: migrate this test to JUnit 5. Constraint: PowerMock is not supported with JUnit 5. So the tools we can most likely use to replace the use of powermock are reflection either from java itself or the libraries: spring test ReflectionUtils, or powermock.reflect Whitebox (which supports setting static final fields) If you would keep the test as JUnit 4 but remove the use of PowerMock on the current master that would already solve our issue. We can then simply turn it into JUnit 5 in a few minutes. |
…ng without PowerMock
@teleivo you can merge it before we can continue . |
thank you very much @WolfSchlegel !! |
thank you very much @WolfSchlegel |
Description of what I changed
Issue I worked on
see https://issues.openmrs.org/browse/TRUNK-5817
Checklist: I completed these to help reviewers :)
My IDE is configured to follow the code style of this project.
No? Unsure? -> configure your IDE, format the code and add the changes with
git add . && git commit --amend
I have added tests to cover my changes. (If you refactored
existing code that was well tested you do not have to add tests)
No? -> write tests and add them to this commit
git add . && git commit --amend
I ran
mvn clean package
right before creating this pull request andadded all formatting changes to my commit.
No? -> execute above command
All new and existing tests passed.
No? -> figure out why and add the fix to your commit. It is your responsibility to make sure your code works.
My pull request is based on the latest changes of the master branch.
No? Unsure? -> execute command
git pull --rebase upstream master