Testing Dropwizard 'Application' class - java

I'm looking to test the Dropwizard 'Application' class without bootstrapping an entire Dropwizard server.
I'd essentially just like to ensure that the one bundle I'm registering is registered successfully.
All the routes I've been down so far result in NullPointer exceptions due to various other components not being setup correctly. Is there an easy path here?
public class SentimentApplication extends Application<SentimentConfiguration> {
public static void main(final String[] args) throws Exception {
new SentimentApplication().run(args);
}
#Override
public String getName() {
return "Sentiment";
}
#Override
public void initialize(final Bootstrap<SentimentConfiguration> bootstrap) {
bootstrap.setConfigurationSourceProvider(
new SubstitutingSourceProvider(bootstrap.getConfigurationSourceProvider(),
new EnvironmentVariableSubstitutor(false)
)
);
}
#Override
public void run(final SentimentConfiguration configuration,
final Environment environment) {
// TODO: implement application
}
}

You can register a simple command and call run method of your application with that command instead of server command. That way your application will be executed without running a server.
I wanted to do sth similar to what you want. (Considering ExampleApp as main Application class of my code) I wanted to write a test to make sure that there is no exception parsing the configuration. (Because KotlinModule() should have beed registered to environment.objectMaooer in initialize method of app, otherwise we would have had a runtime error.) I achieved it with sth similar to:
import io.dropwizard.cli.EnvironmentCommand
import io.dropwizard.setup.Bootstrap
import io.dropwizard.setup.Environment
import com.example.config.ExampleConfiguration
import net.sourceforge.argparse4j.inf.Namespace
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class DummyCommand(app: DummyApp) : EnvironmentCommand<ExampleConfiguration>(app, "dummy", "sample test cmd") {
var parsedConfig: ExampleConfiguration? = null
override fun run(environment: Environment, namespace: Namespace, configuration: ExampleConfiguration) {
parsedConfig = configuration
}
}
class DummyApp : ExampleApp() {
val cmd: DummyCommand by lazy { DummyCommand(this) }
override fun initialize(bootstrap: Bootstrap<ExampleConfiguration>) {
super.initialize(bootstrap)
bootstrap.addCommand(cmd)
}
}
class ExampleAppTest {
#Test
fun `Test ExampleConfiguration is parsed successfully`() {
val app = DummyApp()
app.run("dummy", javaClass.getResource("/example-app-test/test-config.yml").file)
val config = app.cmd.parsedConfig
assertNotNull(config)
assertEquals("foo", config.nginxUsername)
}
}

Related

#PropertySource(factory=...) breaks #SpringBootTest when loading META-INF/build-info.properties

I am trying to setup a custom #ConfigurationProperties class loaded from a HOCON syntax .conf file.
I have a Class annotated with #PropertySource(factory=TypesafePropertySourceFactory.class, value = "classpath:app.conf")
#Configuration
#ConfigurationProperties(value = "app.server")
#PropertySource(factory = TypesafePropertySourceFactory.class, value = "classpath:app.conf")
public class ServerProperties {
public int port;
}
and a simple test class:
#SpringBootTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeTest {
#Test
public void someCoolTest() {/* ... */}
// ...
}
When i run my junit test runner, i get the following error:
Caused by: com.typesafe.config.ConfigException$BadPath: path parameter: Invalid path 'spring.info.build.location:classpath:META-INF/build-info.properties': Token not allowed in path expression: ':' (you can double-quote this token if you really want it here)
at com.typesafe.config.impl.PathParser.parsePathExpression(PathParser.java:155) ~[config-1.4.0.jar:1.4.0]
at com.typesafe.config.impl.PathParser.parsePathExpression(PathParser.java:74) ~[config-1.4.0.jar:1.4.0]
at com.typesafe.config.impl.PathParser.parsePath(PathParser.java:61) ~[config-1.4.0.jar:1.4.0]
...
If i uncomment the #PropertySource line on the ServerProperties class, the tests proceed normally. It seems strange to me that my custom PropertySourceFactory gets in the way of the default .properties file resolution process.
PropertySource and Factory classes
// TypesafeConfigPropertySource.java
import com.typesafe.config.Config;
import org.springframework.core.env.PropertySource;
public class TypesafeConfigPropertySource extends PropertySource<Config> {
public TypesafeConfigPropertySource(String name, Config source) {
super(name, source);
}
#Override
public Object getProperty(String path) {
if (source.hasPath(path)) {
return source.getAnyRef(path);
}
return null;
}
}
// TypesafePropertySourceFactory.java
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import java.io.IOException;
import java.util.Objects;
public class TypesafePropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
Config config = ConfigFactory.load(Objects.requireNonNull(resource.getResource().getFilename())).resolve();
String safeName = name == null ? "typeSafe" : name;
return new TypesafeConfigPropertySource(safeName, config);
}
}
Am I missing something fundamental about configuring custom property resource factories, or is this a bug?
Versions
Spring boot 2.3.4
Junit Jupiter 5.6.2
Maybe you can also solve it with the use of a ContextInitializer as suggested in the answer here:
Spring Environment backed by Typesafe Config
TL;DR
Return null if you cannot process the path in your custom impl
public class TypesafeConfigPropertySource extends PropertySource<Config> {
// ...
#Override
public Object getProperty(String path) {
try {
if (source.hasPath(path)) {
return source.getAnyRef(path);
}
} catch(ConfigException.BadPath ignore) {
}
return null;
}
// ...
}
Explanation
I am making educated guesses, but functionally this appears supported by the way the code behaves
the most likely scenario here is the resolution order will consider our custom implementation before any default implementation. The method used in our implementation will error out with any path containing a ":" and "[" as the error occurs in the check for the path's existence.
I'm simply wrapping the BadPath exception in order to catch any problem and then returning null to signify no match.

Spring-Boot #Autowired variable is null

I'm trying to get a repository into a class annotated with #Service using the #Autowired annotation in a Spring-boot class. However, the repository turns up as a null.
Here's the relevant code:
#Service
public class ImportLicenses {
#Autowired
private LicenseRepository licenseRepository;
public static void main (String[] args) {
ImportLicenses importLicenses = new ImportLicenses();
importLicenses.listFiles("/Users/admin/Licenses/licenses");
}
private void processLicense (Path path) {
try {
count++;
BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class);
FileTime fileTime = attr.lastModifiedTime();
Permission permission = new Permission(readLineByLineJava8(path.toFile().getAbsolutePath()));
LicensePojo licensePojo = new LicensePojo(permission, path);
Optional<License> licenseOptional = licenseRepository.findById(licensePojo.getId());
at which point it gets an NPE since licenceReposity is null.
I am able to access the licenseRepository in a controller class with this constructor
public LicenseController(LicenseRepository licenseRepository,
UserRepository userRepository) {
this.licenseRepository = licenseRepository;
this.userRepository = userRepository;
}
However, since I'm calling the constructor directly in the static main method, this doesn't seem like it's available. What's the best way to get the repository into this class?
Edit: Thanks for the responses. To clarify the question, I'm trying to pull this class into the structure of the existing Spring Boot application, instead of creating a separate one.
Option 1: Create a button or menu selection on the UI, and create a new controller class to run the import. This would be the simplest, but I don't want to necessarily have that on the UI.
Option 2: Code the import class create another Spring Application
#SpringBootApplication
public class ImportLicenses implements ApplicationRunner {
private final Logger logger = LoggerFactory.getLogger(LicenseGenApplication.class);
#SpringBootApplication
public static void main() {
main();
}
public static void main(String[] args) {
SpringApplication.run(ImportLiceneses.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
listFiles("/Users/admin//licenses");
}
public void listFiles(String path) {
try {
Files.walk(Paths.get(path))
.filter(ImportLicenses::test)
.forEach(p -> processLicense(p));
} catch (IOException e) {
e.printStackTrace();
}
}
....
}
Option 3 - Create a non-executable jar file from the existing application for use in the new application to avoid duplicating code.
Option 1 is the quickest, I'm not sure if option 2 work, Option 3 I'll take a look at to see if it's do-able.
Your application is a usual Java application. It is not a Spring Boot application.
What should you do? Make a Spring Boot application of it. For instance, create a demo app at https://start.spring.io/, compare your main class to the main class in the demo application, then adjust your main class correspondingly. Also compare your Maven or Gradle config to the config of the demo app and adjust correspondingly.
At least, your application should have an annotation #SpringBootApplication. Also, it should be launched via SpringApplication.run(...). This is a minimum. Depending on what you need, you may want to use #EnableAutoConfiguration and other configuration options.

Running a specific #Issue via gradle

I am using Spock to write tests and Gradle to run them. I annotate them using #Issue. Tests for the same issue are not necessarily in the same file:
FooTest.groovy:
class FooTest extends Specification {
#Issue("FOOBAR-123")
def "should foo"() {
...
}
#Issue("FOOBAR-234")
def "should bar"() {
...
}
}
BarTest.groovy:
class BarTest extends Specification {
#Issue("FOOBAR-123")
def "should quux"() {
...
}
}
I would like to run all tests for a single issue (FOOBAR-123).
In rspec it would be easy:
describe 'foo' do
it "should foo", foobar-123: true do
...
end
rspec --tag foobar-123
But I can't see how to do this with Spock and Gradle.
This is possible through the use of extensions. You can do that by creating a file in your project
IssueIncludeExtension.groovy
package com.tarun.lalwani
import org.spockframework.runtime.extension.AbstractGlobalExtension
import org.spockframework.runtime.model.FeatureInfo
import org.spockframework.runtime.model.SpecInfo
import spock.lang.Issue
import java.lang.annotation.Annotation
class IssueIncludeExtension extends AbstractGlobalExtension{
#Override
void visitSpec(SpecInfo spec) {
def issuesList
issuesList = System.properties["spock.issues"]
if (issuesList) {
def arrIssues = issuesList.split(",").toList()
System.out.println('I was called')
for (FeatureInfo feature : spec.getAllFeatures())
{
def method, ann;
method = feature.getFeatureMethod();
def issueAnnotation = method.getAnnotation(Issue.class);
if (issueAnnotation) {
if (issueAnnotation.value().size() > 0)
{
if (issueAnnotation.value().toList().intersect(arrIssues))
{
//we have a matching issue
feature.setExcluded(false)
} else {
feature.setExcluded((true))
}
}
} else {
// this doesn't belong to any issue
feature.setExcluded(true)
}
}
} else {
super.visitSpec(spec)
}
}
}
Then in create below file
META-INF/services/org.spockframework.runtime.extension.IGlobalExtension
com.tarun.lalwani.IssueIncludeExtension
After that you can update your gradle script and add a test
task issueTest(type: Test) {
// This task belongs to Verification task group.
group = 'Verification'
// Set Spock configuration file when running
// this test task.
systemProperty 'spock.issues', 'Issue4'
}
Now I have below tests in groovy
package com.mrhaki.spock
import spock.lang.Issue
import spock.lang.Specification
class WordRepositorySpec extends Specification {
#Remote // Apply our Remote annotation.
#Issue(["Issue1", "Issue2"])
def "test remote access"() {
given:
final RemoteAccess access = new RemoteAccess()
expect:
access.findWords('S') == ['Spock']
}
#Issue("Issue4")
def "test local access"() {
given:
final LocalAccess access = new LocalAccess()
expect:
access.findWords('S') == ['Spock']
}
}
Running the test just runs Issue4 related test
According to this article, you may create a configuration for any annotation.
Unfortunately (after some tries) I wasn't able to filter tests against a value of the annotation, the IncludeExcludeCriteria class only accepts classes or annotations.
runner {
include spock.lang.Issue
}
I think you might get around it with creating annotations per issue and using Annotations Collectors.

Selenium Java - How to outsource #Config and call tests from an extern class?

I'm sure, this question can be answered very quickly by a experienced Java-developer. But as I am not that familiar with Java I don't get how to source out the #Config part of Selenium in Java. It would be optimal, if I could have a config-file or -class where I can put the data (browser, website etc.) on the one hand and on the other hand the test-files.
Here is an example of a test file:
package com.example_test.selenium;
import io.ddavison.conductor.Browser;
import io.ddavison.conductor.Config;
import io.ddavison.conductor.Locomotive;
import org.junit.Test;
#Config(
browser = Browser.CHROME,
url = "http://example.com"
)
public class test_a_Home extends Locomotive {
#Test
public void testifExists() {
validatePresent(site_a_Home.EL_NEWCUSTOMERBANNER);
}
}
Now I would like to have a seperate file called tests.java where I can call the "test_a_Home"-function. If I try it just with
package com.example_test.selenium;
public class tests {
test_a_Home test = new test_a_Home();
test.testifExists();
}
I am receiving the error, that "testifExists()" cannot be resolved.
I tried changing the public void testifExists() to public int testifExists() and tried to call it with int res = test.testifExists(); in the class tests but this does not work either, as I receive the error java.lang.Exception: Method testNewCustomersBannerExists() should be void.
I would be very happy, if anyone could help me. If you need more information please feel free to mention it. Thank you.
If you want your design to be like this, then you need to organize your tests as such:
public class BasePage {
public Locomotive test;
public BasePage(Locomotive baseTest) {
test = baseTest;
}
}
public class test_a_Home extends BasePage {
public test_a_Home(Locomotive baseTest) {
super(baseTest);
}
public void testifExists() {
test.validatePresent(site_a_Home.EL_NEWCUSTOMERBANNER);
}
}
Then your test class, i'd recommend creating a base class as well:
#Config(
browser = Browser.CHROME,
url = "http://example.com"
)
public class BaseTest extends Locomotive {}
And then your test class:
public class tests extends BaseTest {
test_a_Home test = new test_a_Home(this);
#Test
public void testHomePage() {
test.testIfExists();
}
}
Also you state state:
I don't get how to source out the #Config part of Selenium in Java.
Please make sure you know, that using Conductor abstracts you from the Selenium API.. It just wraps it. #Config does not belong to Selenium, it belongs to Conductor.

AfterAll global hook cucumber-jvm

I'm using cucumber-jvm in my integration tests and I need to execute some code after all scenarios are finished, just once.
After reading carefully some posts like this and reviewed this reported issue, I've accomplished it doing something like this:
public class ContextSteps {
private static boolean initialized = false;
#cucumber.api.java.Before
public void setUp() throws Exception {
if (!initialized) {
// Init context. Run just once before first scenario starts
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
// End context. Run just once after all scenarios are finished
}
});
initialized = true;
}
}
}
I think the context initialization (equivalent to BeforeAll) done in this way is fine. However, although it's working, I'm not sure at all if the AfterAll simulation using Runtime.getRuntime().addShutdownHook() is a good practice.
So, these are my questions:
Should I avoid Runtime.getRuntime().addShutdownHook() to implement AfterAll?
Are there other better choices to emulate the AfterAll behaviour in cucumber-jvm?
Thanks in advance for the help.
Probably a better way would be to use a build tool, like Ant, Maven or Gradle for set-up and tear-down actions, which are part of integration tests.
When using Maven Fail Safe Plug-in, for setting up integration tests. There is the phase pre-integration-test, which is typically used for setting up the database and launch the web-container. Then the integration-tests are run (phase integration-test). And afterwards the phase post-integration-test is run, for shutting down and closing / removing / cleaning up things.
INFO In case the Cucumber tests are run through JUnit, the following might also be worth considering
In case it is simpler, smaller set up stuff, you can have a look at the JUnit #BeforeClass and #AfterClass. Or implement a JUnit #ClassRule, which has it's own before() and after() methods.
#ClassRule
public static ExternalResource resource = new ExternalResource() {
#Override
protected void before() throws Throwable {
myServer.connect();
}
#Override
protected void after() {
myServer.disconnect();
}
};
As of Cucumber 2.4.0, the following class gave me the equivalent of #BeforeClass in Junit.
You can place this in test/java/cucumber/runtime.
package cucumber.runtime; //cannot change. can be under /test/java
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.snippets.FunctionNameGenerator;
import gherkin.pickles.PickleStep;
import java.util.List;
public class NameThisClassWhatever implements Backend {
private Glue glue;
private List<String> gluePaths;
#Override
public void loadGlue(Glue glue, List<String> gluePaths) {
this.glue = glue;
this.gluePaths = gluePaths;
//Any before steps here
}
#Override
public void disposeWorld() {
//any after steps here
}
#Override
public void setUnreportedStepExecutor(UnreportedStepExecutor executor) { }
#Override
public void buildWorld() { }
#Override
public String getSnippet(PickleStep p, String s, FunctionNameGenerator fng) {
return null;
}
public NameThisClassWhatever(ResourceLoader resourceLoader) { }
}

Categories

Resources