Would anyone have any objection to enrich BaseContextSensitiveTest for it to mock started modules?
I just tried this locally and it worked well:
public BaseContextSensitiveTest() {
...
Context.setRuntimeProperties(props);
loadCount++;
for (Module mod : getModulesToStart()) {
startModule(mod);
}
}
Where the new overridable getModulesToStart() provides the modules to appear started during the test (and defaults to an empty list in the base class.)
As I was playing around with @OpenmrsProfile(modules = {...}), I found that the above idea would help a lot mimic in tests what actually happens at runtime with ModuleFactory. It basically ensures that the modules are marked as started before the Spring context is initialized.
Anyway I can see that ModuleFactory.startedModules is still empty when OpenmrsProfileExcludeFilter.matchOpenmrsProfileAttributes(..) is reached. Am I missing something?
Also this test doesnât pass:
@StartModule( { "org/openmrs/module/include/exti18n-1.0.0.omod" })
public class MyTest extends BaseModuleContextSensitiveTest {
@Test
public void should() {
Assert.assertTrue(ModuleFactory.isModuleStarted("exti18n"));
}
}
ERROR - ModuleUtil.startup(112) |2017-08-28 18:25:47,006| Unable to load module at path:
org/openmrs/module/include/exti18n-1.0.0.omod because no file exists there and it is not found on the classpath. (absolute path tried: /org/openmrs/module/include/exti18n-1.0.0.omod)
So it looks like an actual .omod should be there as a resourceâŠ
I would hope itâs not the way it works, I was expecting something more âmockâ so to say. Because if one needs to compile and build dummy OMODs for the sake of unit tests, that could provide to be impractical.
Yes, you would need to add the omod to the project for it to work and yes I see a downside to it but it can be improved so that the test framework fetches it from the local maven repo.
If you want some sort of mock then you might as well just mock things you need the regular mock way.
Until now I was loading an actual OMOD without using the annotation that you pointed me to (see here).
I will check again to confirm whether @OpenmrsProfile processes this as expected or not.
But I would favour something a lot simpler, like overriding a method of a unit test when needed; such as:
@wyclif, it looks like oneâs got to choose. The test is run either with SpringJUnit4ClassRunner or with PowerMockRunner.
Alternatively this kind of thing is possible:
@PowerMockRunnerDelegate(PowerMockRunner.class)
@PrepareForTest(ModuleFactory.class)
public class MyTest extends BaseModuleContextSensitiveTest {
@Before
public void setup() {
mockStatic(ModuleFactory.class);
when(ModuleFactory.isModuleStarted(any(String.class))).thenReturn(true);
}
@Test
public void should() {
Assert.assertTrue(ModuleFactory.isModuleStarted("mymodule"));
}
}
However this would require upgrading powermock-module-junit4 in Core:
@dkayiwa, I think what could benefit a wider audience, is to expose an overridable method in BaseContextSensitiveTest that would let developers perform some pre-Spring loading actions.
Some sort of
protected void setupBeforeContext() {
}
And that method would be called last in its constructor.
Just trying to brainstorm a little before the fact.
Perhaps is there already a way to intercept moments before Spring loading? In which case the PR would be useless.
I eventually settled on a very simple solution consisting in âtouchingâ the started modules map of ModuleFactory in the constructor of my context-sensitive test:
public class MyTest extends BaseModuleContextSensitiveTest {
/*
* pre-Spring loading setup
*/
public MyTest() {
super();
ModuleFactory.getStartedModulesMap()
.put("key", new Module("", "moduleid", "", "", "", "1.0.0") );
}
...
}
This has required to mark some key tests with the Spring test annotation @DirtiesContext in order to make sure that they start from a clean slate.
For reference, I will report back the relevant commit here when things have been PRâd.
@wyclif following your remark I dug further and even though this instruction is called in StartModuleExecutionListener, the conditional bean is not reloaded.
The context:
A component class has an @OpenmrsProfile annotation with a condition on a module.
A unit test starts the module with @StartModule.
This is what happens:
OpenmrsProfileExcludeFilter is called first loading the bean âconditionallyâ, but at that point the module is not started yet.
The module is started.
ctx.refresh() is called in StartModuleExecutionListener.
OpenmrsProfileExcludeFilter is not revisited and the bean depending on the condition on the module remains loaded as in 1.
Itâs possible that itâs a bug given that conditional resource loading was added at a later point and possibly the StartModuleExecutionListener was never updated accordingly.
@dkayiwa, would it be possible for you to look at our branch TRUNK-5213 (here) and tell me why this one test doesnât pass: OpenmrsProfileExcludeFilterWithModulesTest?
For you reference, here are the key classes/files that I have changed and why:
TestingApplicationContext.xml so that it can be reloaded within tests to perform a âcomponent scanâ, here.
StartModuleExecutionListener that triggers the component scan prior to refreshing the context, here.
OpenmrsProfileWithoutTest1Module, the conditional resource, here.
OpenmrsProfileExcludeFilterWithModulesTest, the new (failing) test that starts the module on which there is a condition, here.
I can see that everything seems to happen in order, except that at the end of the day, the conditional bean can still be fetched from the contextâŠ