Any got Spring Boot working with cucumber-jvm? - cucumber-jvm

I'm using spring boot as it removes all the boring stuff and let's me focus on my code, but all the test examples use junit and I want to use cucumber?
Can someone point me in the right direction to get cucumber and spring to start things up, do all the auto config and wiring and let my step definitions use auto wired beans to do stuff?

Try to use the following on your step definition class:
#ContextConfiguration(classes = YourBootApplication.class,
loader = SpringApplicationContextLoader.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class MySteps {
//...
}
Also make sure you have the cucumber-spring module on your classpath.

Jake - my final code had the following annotations in a superclass that each cucumber step definition class extended, This gives access to web based mocks, adds in various scopes for testing, and bootstraps Spring boot only once.
#ContextConfiguration(classes = {MySpringConfiguration.class}, loader = SpringApplicationContextLoader.class)
#WebAppConfiguration
#TestExecutionListeners({WebContextTestExecutionListener.class,ServletTestExecutionListener.class})
where WebContextTestExecutionListener is:
public class WebContextTestExecutionListener extends
AbstractTestExecutionListener {
#Override
public void prepareTestInstance(TestContext testContext) throws Exception {
if (testContext.getApplicationContext() instanceof GenericApplicationContext) {
GenericApplicationContext context = (GenericApplicationContext) testContext.getApplicationContext();
ConfigurableListableBeanFactory beanFactory = context
.getBeanFactory();
Scope requestScope = new RequestScope();
beanFactory.registerScope("request", requestScope);
Scope sessionScope = new SessionScope();
beanFactory.registerScope("session", sessionScope);
}
}
}

My approach is quite simple. In a Before hook (in env.groovy as I am using Cucumber-JVM for Groovy), do the following.
package com.example.hooks
import static cucumber.api.groovy.Hooks.Before
import static org.springframework.boot.SpringApplication.exit
import static org.springframework.boot.SpringApplication.run
def context
Before {
if (!context) {
context = run Application
context.addShutdownHook {
exit context
}
}
}

Thanks to #PaulNUK, I found a set of annotations that will work.
I posted the answer in my question here
My StepDefs class required the annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = DemoApplication.class, loader = SpringApplicationContextLoader.class)
#WebAppConfiguration
#IntegrationTest
There is also a repository with source code in answer I linked.

Related

In-memory H2 database, insert not working in SpringBootTest

I have a SpringBootApplicationWhich I wish to test.
Below are the details about my files
application.properties
PRODUCT_DATABASE_PASSWORD=
PRODUCT_DATABASE_USERNAME=sa
PRODUCT_DATABASE_CONNECTION_URL=jdbc:h2:file:./target/db/testdb
PRODUCT_DATABASE_DRIVER=org.h2.Driver
RED_SHIFT_DATABASE_PASSWORD=
RED_SHIFT_DATABASE_USERNAME=sa
RED_SHIFT_DATABASE_CONNECTION_URL=jdbc:h2:file:./target/db/testdb
RED_SHIFT_DATABASE_DRIVER=org.h2.Driver
spring.datasource.platform=h2
ConfigurationClass
#SpringBootConfiguration
#SpringBootApplication
#Import({ProductDataAccessConfig.class, RedShiftDataAccessConfig.class})
public class TestConfig {
}
Main Test Class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {TestConfig.class,ConfigFileApplicationContextInitializer.class}, webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MainTest {
#Autowired(required = true)
#Qualifier("dataSourceRedShift")
private DataSource dataSource;
#Test
public void testHourlyBlock() throws Exception {
insertDataIntoDb(); //data sucessfully inserted
SpringApplication.run(Application.class, new String[]{}); //No data found
}
}
Data Access In Application.class;
try (Connection conn = dataSourceRedShift.getConnection();
Statement stmt = conn.createStatement() {
//access inserted data
}
Please Help!
PS for the spring boot application the test beans are being picked so bean instantiation definitely not a problem. I think I am missing some properties.
I do not use hibernate in my application and data goes off even within the same application context (child context). i.e. I run a spring boot application which reads that data inserted earlier
Problem solved.
removing spring.datasource.platform=h2 from the application.properties.
Made my h2 data persists.
But I still wish to know how is h2 starting automatically?

Hazelcast No DataSerializerFactory registered for namespace: 0 on standalone process

Trying to set a HazelCast cluster with tcp-ip enabled on a standalone process.
My class looks like this
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
int personId;
String name;
Person(){};
//getters and setters
}
Hazelcast is loaded as
final Config config = createNewConfig(mapName);
HazelcastInstance node = Hazelcast.newHazelcastInstance(config);`
Config createNewConfig(mapName){
final PersonStore personStore = new PersonStore();
XmlConfigBuilder configBuilder = new XmlConfigBuilder();
Config config = configBuilder.build();
config.setClassLoader(LoadAll.class.getClassLoader());
MapConfig mapConfig = config.getMapConfig(mapName);
MapStoreConfig mapStoreConfig = new MapStoreConfig();
mapStoreConfig.setImplementation(personStore);
return config;
}
and my myhazelcast config has this
<tcp-ip enabled="true">
<member>machine-1</member>
<member>machine-2</member>
</tcp-ip>
Do I need to populate this tag in my xml?
I get this error when a second instance is brought up
com.hazelcast.nio.serialization.HazelcastSerializationException: No DataSerializerFactory registered for namespace: 0
2275 at com.hazelcast.nio.serialization.DataSerializer.read(DataSerializer.java:98)
2276 at com.hazelcast.nio.serialization.DataSerializer.read(DataSerializer.java:39)
2277 at com.hazelcast.nio.serialization.StreamSerializerAdapter.read(StreamSerializerAdapter.java:41)
2278 at com.hazelcast.nio.serialization.SerializationServiceImpl.toObject(SerializationServiceImpl.java:276)
Any help is highly appericiated.
Solved my problem, I had a pom.xml with hazelcast-wm so I did not have actual hazelcast jar in my bundled jar. Including that fixed my issue.
Note that this same "No DataSerializerFactory registered for namespace: 0" error message can also occur in an OSGi environment when you're attempting to use more than one Hazelcast instance within the same VM, but initializing the instances from different bundles. The reason being that the com.hazelcast.util.ServiceLoader.findHighestReachableClassLoader() method will sometimes pick the wrong class loader during Hazelcast initialization (as it won't always pick the class loader you set on the config), and then it ends up with an empty list of DataSerializerFactory instances (hence causing the error message that it can't find the requested factory with id 0). The following shows a way to work around that problem by taking advantage of Java's context class loader:
private HazelcastInstance createHazelcastInstance() {
// Use the following if you're only using the Hazelcast data serializers
final ClassLoader classLoader = Hazelcast.class.getClassLoader();
// Use the following if you have custom data serializers that you need
// final ClassLoader classLoader = this.getClass().getClassLoader();
final com.hazelcast.config.Config config = new com.hazelcast.config.Config();
config.setClassLoader(classLoader);
final ClassLoader previousContextClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(classLoader);
return Hazelcast.newHazelcastInstance(config);
} finally {
if(previousContextClassLoader != null) {
Thread.currentThread().setContextClassLoader(previousContextClassLoader);
}
}
}

How to find port of Spring Boot container when running a spock test using property server.port=0

Given this entry in application.properties:
server.port=0
which causes Spring Boot to chose a random available port, and testing a spring boot web application using spock, how can the spock code know which port to hit?
Normal injection like this:
#Value("${local.server.port}")
int port;
doesn't work with spock.
You can find the port using this code:
int port = context.embeddedServletContainer.port
Which for those interested in the java equivalent is:
int port = ((TomcatEmbeddedServletContainer)((AnnotationConfigEmbeddedWebApplicationContext)context).getEmbeddedServletContainer()).getPort();
Here's an abstract class that you can extends which wraps up this initialization of the spring boot application and determines the port:
abstract class SpringBootSpecification extends Specification {
#Shared
#AutoCleanup
ConfigurableApplicationContext context
int port = context.embeddedServletContainer.port
void launch(Class clazz) {
Future future = Executors.newSingleThreadExecutor().submit(
new Callable() {
#Override
public ConfigurableApplicationContext call() throws Exception {
return (ConfigurableApplicationContext) SpringApplication.run(clazz)
}
})
context = future.get(20, TimeUnit.SECONDS);
}
}
Which you can use like this:
class MySpecification extends SpringBootSpecification {
void setupSpec() {
launch(MyLauncher.class)
}
String getBody(someParam) {
ResponseEntity entity = new RestTemplate().getForEntity("http://localhost:${port}/somePath/${someParam}", String.class)
return entity.body;
}
}
The injection will work with Spock, as long as you've configured your spec class correctly and have spock-spring on the classpath. There's a limitation in Spock Spring which means it won't bootstrap your Boot application if you use #SpringApplicationConfiguration. You need to use #ContextConfiguration and configure it manually instead. See this answer for the details.
The second part of the problem is that you can't use a GString for the #Value. You could escape the $, but it's easier to use single quotes:
#Value('${local.server.port}')
private int port;
Putting this together, you get a spec that looks something like this:
#ContextConfiguration(loader = SpringApplicationContextLoader, classes = SampleSpockTestingApplication.class)
#WebAppConfiguration
#IntegrationTest("server.port=0")
class SampleSpockTestingApplicationSpec extends Specification {
#Value("\${local.server.port}")
private int port;
def "The index page has the expected body"() {
when: "the index page is accessed"
def response = new TestRestTemplate().getForEntity(
"http://localhost:$port", String.class);
then: "the response is OK and the body is welcome"
response.statusCode == HttpStatus.OK
response.body == 'welcome'
}
}
Also note the use of #IntegrationTest("server.port=0") to request a random port be used. It's a nice alternative to configuring it in application.properties.
You could do this too:
#Autowired
private org.springframework.core.env.Environment springEnv;
...
springEnv.getProperty("server.port");

How do I get Xtext's model from a different plugin?

I've written an Xtext-based plugin for some language. I'm now interested in creating a new independent view (as a separate plugin, though it requires my first plugin), which will interact with the currently-active DSL document - and specifically, interact with the model Xtext parsed (I think it's called the Ecore model?). How do I approach this?
I saw I can get an instance of XtextEditor if I do something like this when initializing my view:
getSite().getPage().addPartListener(new MyListener());
And then, in MyListener, override partActivated and partInputChanged to get an IWorkbenchPartReference, which is a reference to the XtextEditor. But what do I do from here? Is this even the right approach to this problem? Should I instead use some notification functionality from the Xtext side?
Found it out! First, you need an actual document:
IXtextDocument doc = editor.getDocument();
Then, if you want to access the model:
doc.modify(new IUnitOfWork.Void<XtextResource>() { // Can also use just IUnitOfWork
#Override public void process(XtextResource state) throws Exception {
...
}
});
And if you want to get live updates whenever it changes:
doc.addModelListener(new IXtextModelListener() {
#Override public void modelChanged(XtextResource resource) {
for (EObject model : resource.getContent()) {
...
}
}
});

Eclipse: Within a plug-in, how to access another plug-ins preference store?

I have an Eclipse plug-in with a checkbox in the plug-in's preference page.
This checkbox is used for enabling and disabling an editor, which is being launched from this plug-in.
However, the problem is, I would also like to be able to enable and disable this 'editor-launch' from another plug-in, by having actions which change the value of the checkbox in the above mentioned preference page.
Here's the problem, how do I access that local preference store from another plug-in?
I've tried things like..
View myView = (View) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().findView("ViewID");
But this 'myView' always seems to be null.. And also, what would I do with the view since it's the Plug-in I want.
Platform.getBundle('bundleName')...
Same here, want the Plugin, not the bundle corresponding to is.
No matter what I try nothing seems to work.
Does anyone have any ideas?
There are two ways of doing this:
Please refer to http://www.vogella.com/tutorials/EclipsePreferences/article.html#preferences_pluginaccess
Using .getPluginPreferences(). For example, there is a plugin class "com.xxx.TestPlugin" which extends org.eclipse.ui.plugin.AbstractUIPlugin.Plugin, in order to get access to the preferences of TestPlugin. The plugin code could be below:
public class TestPlugin extends AbstractUIPlugin {
private static TestPlugin plugin;
public static final String PREF_TEST = "test_preference";
/**
* The constructor.
*/
public TestPlugin() {
plugin = this;
}
/**
* This method is called upon plug-in activation
*/
public void start(BundleContext context) throws Exception {
super.start(context);
}
/**
* This method is called when the plug-in is stopped
*/
public void stop(BundleContext context) throws Exception {
super.stop(context);
plugin = null;
}
/**
* Returns the shared instance.
*/
public static TestPlugin getDefault() {
return plugin;
}
}
To access the preference of TestPlugin, the code could be:
TestPlugin.getDefault().getPluginPreferences().getDefaultBoolean(TestPlugin.PREF_TEST);
Or have a look at this answer: Writing Eclipse plugin to modify Editor Preferences
This thread recommend the use of a Service tracker:
ServiceTracker tracker = new ServiceTracker(ToolkitPlugin.getDefault().getBundle().getBundleContext(),
IProxyService.class.getName(), null);
tracker.open();
proxyService = (IProxyService) tracker.getService();
proxyService.addProxyChangeListener(this);
This may work.
Prefs stores are found per plugin. This is one way to get a prefs store for the plugin whose activator class is ActivatorA.
IPreferenceStore store = ActivatorA.getDefault().getPreferenceStore();
If you want another plugin to refer to the same store, perhaps you could expose some api on ActivatorA for it to get there, e.g.
public IPreferenceStore getSharedPrefs() {
return ActivatorA.getDefault().getPreferenceStore();
}
The second plugin would find the shared store by doing this
IPreferenceStore sharedPrefs = ActivatorA.getSharedPrefs();
Good luck.