Can I use OSGi Mocks with Declarative Services Annotations - gradle

I'm trying to test an OSGi service annodated with Declaratice Services annotations (org.osgi.service.component.annotations). I have generated my project based on the AEM Multi-Project Example.
public class PostServiceTest {
#Rule
public AemContext context = new AemContext((AemContextCallback) context -> {
context.registerInjectActivateService(new PostService());
}, ResourceResolverType.RESOURCERESOLVER_MOCK);
#Test
public void shouldFetchRandomPosts() {
final PostService postsService = context.getService(PostService.class);
final List<Post> posts = postsService.randomPosts(100);
assertEquals(100, posts.size());
}
}
Whenever I run this test in IntelliJ, OSGi Mocks complain about hte absence of SCR metadata on the tested class.
org.apache.sling.testing.mock.osgi.NoScrMetadataException: No OSGi SCR metadata found for class com.example.PostServiceTest
at org.apache.sling.testing.mock.osgi.OsgiServiceUtil.injectServices(OsgiServiceUtil.java:381)
at org.apache.sling.testing.mock.osgi.MockOsgi.injectServices(MockOsgi.java:148)
at org.apache.sling.testing.mock.osgi.context.OsgiContextImpl.registerInjectActivateService(OsgiContextImpl.java:153)
at org.apache.sling.testing.mock.osgi.context.OsgiContextImpl.registerInjectActivateService(OsgiContextImpl.java:168)
at com.example.PostServiceTest.shouldReturnTheEnpointNamesWithValidConfigurationAsTheListOfAcceptableKeys(PostServiceTest.java:23)
Does this mean I can only test classes annotated with the older SCR annotations that come with Apache Felix? The documentation for OSGi Mocks suggests that Declarative Services annotations is supported in version 2.0.0 and higher. The version I'm using meets this criterion.

Interestingly enough, this only happened when I ran the test directly form the IDE. It turns out that IntelliJ was not generating the SCR metadata when compiling my classes for tests.
When I compile the class under test with Gradle, the 'com.cognifide.aem.bundle' plugin is used to generate the SCR descriptor and place it in the resulting Java Archive. That's why unit tests executed by Gradle work fine. Just clicking the Run button in IntelliJ caused this step to be missed.
In order to get this working, I ended up setting up IntelliJ to allow me to run unit tests via Gradle.
I went to Settings > Build, Execution, Deployment > Gradle > Runner and used the dropdown menu so that I could decide whether to use Gradle on a test-by-test basis.

Related

Cucumber configuration error in IntelliJ: Please annotate a glue class with some context configuration

So I recently started working on Cucumber and have been facing this issue.
This is the hierarchy of my module
As you can see this is submodule in my Spring Boot application (AcceptanceTests), so there are no main methods in it.
This is my CucumberSpringContextConfiguration class
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#CucumberContextConfiguration
public class CucumberSpringContextConfiguration {
}
This is my CucumberIntegrationTest class
#RunWith(Cucumber.class)
#CucumberOptions(
features = "src\test\resources\feature",
plugin = {"pretty", "html:target\\cucumber"},
glue = "com.#####.########.cucumberspringboot.cucumberglue"
)
public class CucumberIntegrationTest {}
I tried running this code with a main class (src/main/java),the code compiled successfully. But since that is not my requirement I removed it and now I am getting below error -
SEVERE: Exception while executing pickle
java.util.concurrent.ExecutionException:
io.cucumber.core.backend.CucumberBackendException: Please annotate a
glue class with some context configuration.
For example:
#CucumberContextConfiguration
#SpringBootTest(classes = TestConfig.class)
public class CucumberSpringConfiguration { }
Or:
#CucumberContextConfiguration
#ContextConfiguration( ... )
public class CucumberSpringConfiguration { }
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at io.cucumber.core.runtime.Runtime.run(Runtime.java:93)
at io.cucumber.core.cli.Main.run(Main.java:92)
at io.cucumber.core.cli.Main.main(Main.java:34)
Caused by: io.cucumber.core.backend.CucumberBackendException:
Please annotate a glue class with some context configuration.
Please suggest how to achieve this without using main class.
io.cucumber.core.cli.Main.run(Main.java:92) at io.cucumber.core.cli.Main.main(Main.java:34)
This part of the stacktrace shows you are using the Cucumber CLI rather than CucumberIntegrationTest. So you are probably running your feature file through IDEA. Presumably you clicked the green play icon in the gutter of a feature file.
Unfortunately Intelij IDEA makes some assumptions and bad guesses. If you look at the run configuration that was created you'll see that there is a command line with --glue argument that probably points at features. You'll have to change this manually.
It is currently unnecessary to guess the --glue argument and I have already asked the team behind Intelij IDEA to fix this at IDEA-243074 but the issue has gotten zero attention so far. You can upvote it, maybe something happens.
You can also avoid this problem by putting your feature files in a different location e.g. src/test/resources/com/#####/########/cucumberspringboot. Becaus this is the parent package of cucumberglue Ingelij IDEA is less likely to mess things up.
Also note: you are currently using JUnit 4 to run Cucumber. Spring Boot prefers using JUnit 5. You should use the Cucumber JUnit Platform Engine. You can find a minimal example here.

maven mojo for reading app classes and generating java

I want to write a maven plugin which will explore the classpath of my application at build time, search for classes with a certain annotation, and generate some java code adding utilities for these classes, which should get compiled in the JAR of the application.
So I wrote a mojo, inheriting from AbstractMojo, and getting the project through:
#Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
I have most of the code, and my mojo does get execute, but I'm having trouble inserting my mojo at right build phase.
If I plug it like that:
#Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES,
requiresDependencyResolution = ResolutionScope.COMPILE)
then the java code which I generate is compiled in the JAR file.
Note that I use project.addCompileSourceRoot to register the output folder.
But that isn't enough for me because it's too early in the build: I cannot read the classpath and find the classes from my project. I think they're not compiled yet.
I search for classes like so:
final List<URL> urls = List.ofAll(project.getCompileClasspathElements())
.map(element -> Try.of(() -> new File(element).toURI().toURL()).get());
final URLClassLoader classLoader = new URLClassLoader(urls.toJavaList().toArray(new URL[0]), Thread.currentThread().getContextClassLoader());
final Set<Class<?>> entities = HashSet.ofAll(new Reflections(classLoader).getTypesAnnotatedWith(MyAnnotation.class));
(I'm using vavr but you get the gist in any case)
So, by plugging my code at the GENERATE_SOURCES phase, this code doesn't work and I don't find any classes.
However, if I plug my mojo at the PROCESS_CLASSES phase:
#Mojo(name = "generate", defaultPhase = LifecyclePhase.PROCESS_CLASSES,
requiresDependencyResolution = ResolutionScope.COMPILE)
Then my classes are found, I can access the rest of the code from the application, but the code that I generate is not taken into account in the build, despite using addCompileSourceRoot.
How do I get both features working at the same time: ability to explore code from the rest of the application and ability to generate code which will be compiled with the rest of the JAR?
I guess a possible answer would be "you can't", but as far as I can tell, querydsl and immmutables are doing it (I tried reading their source but couldn't find the relevant code).
So #khmarbaise was right, what I wanted was not a maven mojo, but rather a maven annotation processor.
I found that this walkthrough was very helpful in creating one, and also this stackoverflow answer came in handy.

JBehave with Serenity - report can't find test scenarios

Using Jbehave, my runner class extends JUnitStories, I can generate the plain-style report with the following:
#Override
public Configuration configuration() {
Class<? extends Embeddable > embeddableClass = this.getClass();
return new MostUsefulConfiguration().useStoryLoader(new LoadFromClasspath(embeddableClass))
.useStoryControls(new StoryControls().doResetStateBeforeScenario(false).useStoryMetaPrefix("story_").useScenarioMetaPrefix("scenario_"))
.useStoryReporterBuilder(
new StoryReporterBuilder()
.withCodeLocation(CodeLocations.codeLocationFromClass(embeddableClass))
.withDefaultFormats().withFormats(CONSOLE, HTML).withFailureTrace(true)
.withFailureTraceCompression(true));
}
Now I want to integrate JBehave with Serenity for better looking reports ^_^. So I changed my runner class to inherit from SerenityStories instead. After adding dependencies and running via maven, the tests pass. However, the Serenity generated report always sees '0 test scenarios'.
I saw that SerenityStories inherits JUnitStories, and overrides the configuration() method as well.
How can I make Serenity see my test scenarios? Do I need to override the configuration() method differently? And how?
Thank you very much!
I was able to make it work by creating a new maven project instead.
Used the archetype: 'serenity-jbehave-archetype'.
There will be a pre-created & empty runner class that inherits SerenityStories.
I just then merged my files to this new project.
As for the runner class, I've overridden the methods stepsFactory() and storyPaths() to match my steps/stories.
Hope that made sense. Thanks!

Intellij, cucumber test and two spring Context Configuration

My project has two different cucumber test and each one needs a different spring context configuration.
The problem I have is that when I run each test individually from Intellij, they load the right Spring Context and the tests are passing, but I press run all test, none of them are passing.
Running a maven test, both test are passing.
this is my code:
#RunWith(FCSCucumber.class)
#Cucumber.Options(strict = true
// , tags = {"~#ignore"}
// , tags = {"#Only"}
, glue ="feature.scenario1"
, features = "src/test/resources/feature/scenario1/"
)
#FCSApplicationProperties(commonProps="config/scenario1/exec.common.properties",
environmentProps="src/test/resources/scenario1-test.properties")
public class TesScenario1Features {
}
#ContextConfiguration("/cucumber-scenario1.xml")
public class scenario1Steps {
......
}
#RunWith(FCSCucumber.class)
#Cucumber.Options(strict = true
// , tags = {"~#ignore"}
// , tags = {"#Only"}
, glue ="feature.scenario2"
, features = "src/test/resources/feature/scenario2/"
)
#FCSApplicationProperties(commonProps="config/scenario2/exec.common.properties",
environmentProps="src/test/resources/scenario2-test.properties")
public class TesScenario2Features {
}
#ContextConfiguration("/cucumber-scenario2.xml")
public class scenario2Steps {
......
}
Thank you very much for your help
The issue is that the IntelliJ cucumber plugin is using the cucumber cli to run tests, without using the JUnit runner at all. This causes several limitations, like requiring the spring annotations on the step definition classes instead of the runner, or by default requiring the steps definitions to be in the same package as the scenario files.
In your example I would actually expect also running a single test to fail, unless the correct application properties are also referenced by the /cucumber-scenario{1,2}.xml files.
The only option I see with the standard cucumber implementation would be to extract the tests into separate projects.
I'm actually working on an alternative implementation of cucumber with an improved spring integration that you might want to try. It's not fully integrated with IntelliJ yet though.

Failed to load ApplicationContext for JUnit test in Hudson Maven, but no issues locally

I've tried just about every configuration I can think of (and reviewed some answers on StackOverflow), but all of our tests show the 'Failed to load ApplicationContext' error when run through Hudson. What is interesting is that some tests appear to run and pass, while some run and fail (as expected), but regardless I'm always getting the errors list for all tests. Here is the basic configuration:
#ContextConfiguration(locations = "classpath:/MyTest-context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class MyTest {
#Autowired
private ApplicationContext applicationContext;
public MyTest() {}
#Test
public void doSomething() {
// Implementation...
}
}
UPDATE:
There appears to be a duplicate set of tests running, one for Emma coverage reporting, and the other the normal tests. It is when the tests run for Emma coverage that they are showing the errors. If I turn off the "emma:emma package" goal so those don't run then I don't get the errors, and the tests appear to run fine. I'm not sure if that helps any.
The answer ended up being close to what gontard was pointing to, which is an issue that was hidden by the way Emma's classloader works. Between my local JUnit tests, what was running in our DEV environment, and what was running in Hudson with Emma, all of them have a different way in which the classloader orders the loading of libraries and classes. I ended up reviewing the stack trace on the test results, and it turns out on my local, a new version of a library was loaded via the POM, but in Hudson Emma was loading an old version of a library first. I had to find and remove the old version, and everything now works fine.

Resources