I have written integration tests in Spock that I would like to reuse for load testing. I haven't had any luck with programmatically executing Spock tests. I need to run an entire spec as a single unit which will be executed concurrently to create load.
Previous posts on stack overflow on this topic are obsolete (I tried a bunch of them with no luck).
Example Spec:
class MySpec extends Specification {
def 'test'() {
expect: 1+1 == 2
}
}
I want to be able to run this in something like (executed, succeeded and failed are AtomicInteger):
executor.submit(() -> {
try {
executed.addAndGet(1);
Result result = mySpecInstance.run() // <-- what should this be.
if (result.wasSuccessful()) {
succeeded.addAndGet(1);
} else {
failed.addAndGet(1);
log.error("Failures encountered: {}", result.getFailures());
}
} catch (RuntimeException e) {
log.error("Exception when running runner!", e);
failed.addAndGet(1);
}});
I've tried the answer in this post which throws
Invalid test class 'my.package.MySpec':
1. No runnable methods]
I tried using the new EmbeddedSpecRunner().run(MySpec.class) which throws
groovy.lang.MissingMethodException: No signature of method: spock.util.EmbeddedSpecRunner.runClass() is applicable for argument types: (Class) values: [class my.package.MySpec]
Possible solutions: getClass(), metaClass(groovy.lang.Closure)
I am using JDK8 with Groovy 3.0.4 and spock-2.0-M3-groovy-3.0 (spock-junit4).
Update:
The answer from post works with Groovy-2.4, Spock-1.3 but not with Groovy-3.0 and Spock-2.0.
Thanks.
Your error did not occur because you used the wrong Spock version, by the way. You can use module spock-junit4 if you want to run the old JUnit 4 API. I just tried, the method works in Spock 1 and still in Spock 2, even though you maybe should upgrade to something that does not rely on an older API and a compatibility layer.
Your error message is simply caused by the fact that you copied & pasted code from the other answer without fixing it. The guy there wrote MySuperSpock.Class which causes the error because if must be MySuperSpock.class with a lower-case "C" or under Groovy simply MySuperSpock because the .class is optional there.
The error message even proves that you had JUnit 4 on the class path and everything was fine, otherwise the code importing JUnit 4 API classes would not have compiled in the first place. And the error message also explains what is wrong and suggests a solution:
Exception in thread "main" groovy.lang.MissingPropertyException: No such property: Class for class: de.scrum_master.testing.MyTest
Possible solutions: class
See? Class MyTest does not have any property called Class. And one possible solution (in this case even the correct one) is to use .class. This gives you a hint. BTW, the syntax MyTest.Class looks like an inner class reference or maybe a property reference to the compiler (to me too).
Update: I just took a closer look and noticed that the solution from the other question you said was working for Spock 1.3 actually compiles and runs, but the JUnit Core runner does not really run the tests. I tried with tests that print something. Furthermore, the result reports all tests as failed.
For simple cases you could use Spock's EmbeddedSpecRunner which is used internally to test Spock itself. Under Spock 1.x it should be enough to have JUnit 4 on the test class path, under Spock 2 which is based on JUnit 5 platform, you need to add these dependencies too because the embedded runner uses them:
<properties>
<version.junit>5.6.2</version.junit>
<version.junit-platform>1.6.2</version.junit-platform>
<version.groovy>3.0.4</version.groovy>
<version.spock>2.0-M3-groovy-3.0</version.spock>
</properties>
<!-- JUnit 5 Jupiter platform launcher for Spock EmbeddedSpecRunner -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>${version.junit-platform}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-testkit</artifactId>
<version>${version.junit-platform}</version>
<scope>test</scope>
</dependency>
Then you can run a test like this:
def spockRunner = new EmbeddedSpecRunner()
def spockResult = spockRunner.runClass(MyTest)
println "Tests run: " + spockResult.runCount
println "Tests ignored: " + spockResult.ignoreCount
println "Tests failed: " + spockResult.failureCount
BTW, the *Count getter methods are deprecated in Spock 2, but they still work. You can replace them by newer ones easily, I just wanted to post code which runs unchanged in both Spock versions 1.x and 2.x.
Update 2: If you want to run the same test e.g. 10x concurrently, each in its own thread, in Groovy a simple way to do that is:
(1..10).collect { Thread.start { new EmbeddedSpecRunner().runClass(MyTest) } }*.join()
Or maybe a bit easier to read with a few line breaks:
(1..10)
.collect {
Thread.start { new EmbeddedSpecRunner().runClass(MyTest) }
}
*.join()
I am assuming that you are familiar with collect (similar to map for Java streams) and the star-dot operator *. (call a method on each item in an iterable).
Related
I deployed a Grails 3.2.0 WAR on Tomcat 8.5.6 and JDK 1.8.0_91 with a simple controller having following code:
package com.test
class MailController {
static responseFormats = ['json']
def index() {
Map headers = (request.headerNames as List).collectEntries { // It fails on this line
return [(it): request.getHeader(it)]
}
println "Incoming email $headers"
render status: 200
}
}
This code fails with the following exception:
Caused by: java.lang.NoClassDefFoundError: groovy/lang/GroovyObject
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at groovy.util.ProxyGenerator.instantiateDelegateWithBaseClass(ProxyGenerator.java:225)
at groovy.util.ProxyGenerator.instantiateDelegateWithBaseClass(ProxyGenerator.java:193)
at groovy.util.ProxyGenerator.instantiateDelegate(ProxyGenerator.java:185)
at groovy.util.ProxyGenerator.instantiateDelegate(ProxyGenerator.java:181)
at org.grails.web.converters.ConverterUtil.invokeOriginalAsTypeMethod(ConverterUtil.java:161)
at org.grails.web.converters.ConvertersExtension.asType(ConvertersExtension.groovy:56)
at com.test.MailController.index(MailController.groovy:7)
at org.grails.core.DefaultGrailsControllerClass$MethodHandleInvoker.invoke(DefaultGrailsControllerClass.java:222)
at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:187)
at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
... 14 common frames omitted
Caused by: java.lang.ClassNotFoundException: groovy.lang.GroovyObject
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
... 27 common frames omitted
Before building the WAR file, I've changed the embedded tomcat to provided in build.gradle and also commented the groovy-ant dependency related to grails-core#10196
I see a answer here but that didn't worked and the above code is working fine when we run via grails run-app.
Update
I shorted down the issue. It is failing on this part only request.headerNames as List
I am pretty sure the problem is with the use of "as List". Mostly because Grails will overwrite Groovy's asType implementation which makes the "as X" coercion syntax work.
Grails does this to add support for things like JSON for marshalling known Grails types to web transport formats.
Unfortunately, in doing so Grails also breaks any asType function you might have declared yourself. Or in this case Groovy itself already declared for converting an Enumeration into a List.
It's quite annoying as Grails is effectively breaking existing contracts here and forcing you to modify upstream code to allow it to run on Grails.
That or dump Grails because it doesn't play nice with perfectly valid Groovy code.
I believe replacing "as List" with .asType(List) won't even fix the issue as you're still invoking the same code. At best you could try .collect([]) {it} instead. It may not be necessary to add the empty array as the first argument to collect.
I'd like to run the JUnit tests in our project from the command line using an Ant target. Up until now we ran the tests manually from Eclipse without any issues using the embedded JUnit in Eclipse.
After finally having figured out how the set the classpath I now get failed Tests for all classes that use the Parameterized Runner from JUnit 4.11.
While running the test target (ant test) the only output is "FAILED" after the name of the Testclass, even with option -v.
Generating testreports shows the following Exception:
java.lang.IllegalArgumentException: wrong number of arguments
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
What does this mean?
The version are:
- JDK 1.6
- ant 1.9.3
- JUnit 4.11
My build.xml is over 400 lines long so I'm not sure if it makes sense to post it here. Let me know if you need parts of the build.xml.
Update 13.05.2015: Here's a sample section from on of our JUnit Test classes that fail. The #Parameters section contains only one entry which is pretty useless in this case but this class still fails when run from ant.
#Parameters
public static Iterable<String[]> testData() {
return Arrays
.asList(new String[][] { { "a-rules-filename" } });
}
#Parameter
public String RULES_FILE_NAME;
Our Test class is annotated like this:
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
#RunWith(Parameterized.class)
public class OurRulesTest {
The Exception is thrown due to a missing constructor in the test class.
It seems that when using the Parameterized Runner you need to specify a constructor with as many arguments as you have parameters.
For some reason this works in Eclipse without specifying a constructor.
I decided to return to Dropwizard after a very long affair with Spring. I quickly got the absolute barebones REST service built, and it runs without any problems.
Using Dropwizard 0.7.1 and Java 1.8, only POM entries are the dropwizard-core dependency and the maven compiler plugin to enforce Java 1.8, as recommended by the Dropwizard user manual
However, as soon as I try to add an Optional QueryParam to the basic controller, the application fails to start with the following error (cut for brevity):
INFO [2015-01-03 17:44:58,059] io.dropwizard.jersey.DropwizardResourceConfig: The following paths were found for the configured resources:
GET / (edge.dw.sample.controllers.IndexController)
ERROR [2015-01-03 17:44:58,158] com.sun.jersey.spi.inject.Errors: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Missing dependency for method public java.lang.String edge.dw.sample.controllers.IndexController.index(java.util.Optional) at parameter at index 0
Exception in thread "main" javax.servlet.ServletException: com.sun.jersey.spi.container.servlet.ServletContainer-6c2ed0cd#330103b7==com.sun.jersey.spi.container.servlet.ServletContainer,1,false
The code for the controller is as follows:
#Path("/")
public class IndexController {
#GET
#Timed
public String index(#QueryParam("name") Optional<String> name) {
String saying = "Hi";
if(name != null && name.isPresent()) {
saying += " " + name.get();
}
return saying;
}
}
If I remove Optional from the mix, the application runs just fine. I replace the Optional-specific code with null checks and it works perfectly.
Am I missing something fundamental here? Both Google Guava Optional and java.util.Optional fail with the same error. (And yes, I did narrow it down to the Optional object)
A quick Google/SO search yielded nothing useful, but feel free to point me to a resource I may have missed
Thanks in advance!
Moments after posting this, I found that the issue was my use of Java 1.8. If using Java 1.8, I have to add the Java8Bundle to my app:
POM Entry:
<dependency>
<groupId>io.dropwizard.modules</groupId>
<artifactId>dropwizard-java8</artifactId>
<version>0.7.0-1</version>
</dependency>
And code in the Application class:
#Override
public void initialize(Bootstrap<SampleConfiguration> bootstrap) {
bootstrap.addBundle(new Java8Bundle());
}
See: https://github.com/dropwizard/dropwizard-java8
This enables both Google Guava Optional and java.util.Optional to work just fine.
If I revert to Java 1.7 and use the Google Guava Optional, it works just fine as well and I don't have to include the Java8Bundle. I'll opt for the Java8Bundle for now, though, as using Java8 features is lucrative for me :)
Cheers!
Running the application with grails run-app works fine but after deploying in Tomcat 7 I get following error.
groovy.lang.MissingMethodException:
No signature of method: static com.digithurst.hdspro.web.Responder.respond()
is applicable for argument types: (ResourceListCmd, QueryCmd, groovy.util.ConfigObject)
values: [ResourceListCmd#5c380e, ...]
Possible solutions: respond(HttpResource, java.lang.Object, java.lang.String)
As already said, this works outside of Tomcat. The way the method is called is exactly as it is implemented. The ResourceListCmd implements the interface HttpResource which makes it a perfect fit. This error also occurs if the first parameter is null.
groovy.lang.MissingMethodException:
No signature of method: static com.digithurst.hdspro.web.Responder.respond()
is applicable for argument types: (null, QueryCmd, groovy.util.ConfigObject)
values: [null, ...]
Possible solutions: respond(HttpResource, java.lang.Object, java.lang.String)
More on the environment:
Windows 7 64 Bit
Java 7 U45 x86
Grails 2.3.4
Tomcat 7.0.47
I have already cleaned the .grails and .m2 folders in the user directory and performed a grails clean berfore creating the war file.
[Edit after answer of H3rnst]
Controller:
def index() {
try {
ResourceListCmd configs = configService.search()
respond Responder.respond(configs, new QueryCmd(level: 'list'),
grailsApplication.config.grails.serverURL)
}
catch (Exception e) {
render status: INTERNAL_SERVER_ERROR
}
}
ResourceListCmd:
interface HttpResource {
...
}
abstract class AbstractHttpResource implements HttpResource {
...
}
class ResourceListCmd extends AbstractHttpResource {
...
}
Responder:
class Responder {
static def respond(HttpResource resource, def query, String serverURL) {
...
}
}
Your war (or tomcat server classpath) contain a duplicate or wrong version of jar that contains the class com.digithurst.hdspro.web.Responder. (The version of the class you are using developing an launching with run-app is different from the one tomcat load running your war)
You could try to unpack the war end verify the version of the problematic jar and/or use a tool like jarscan to scan for duplicate classes.
You could even try to use the command dependecy-report and search for duplicate injection of the same lib. Probably two different plugins your are using are incorporating to differente versions of the same lib causing the problem.
marko's suggestion doing run-war actually gave the final clue to solving this thing. It wasn't a problem with Tomcat but rather the environment the app was running in. run-app as well as run-war both use the "development" environment by default and therefore it worked. Only in "production" it did not, which is what is used when deployed to Tomcat.
The actual culprit was part of the configuration and the error message was right, although unexpected. I'm calling the Responder.respond() method with grailsApplication.config.grails.serverURL. The serverURL was only set in "development" mode but not in "production". That's why Groovy/Java complained about the method signature:
(ResourceListCmd, QueryCmd, groovy.util.ConfigObject) vs (HttpResource, java.lang.Object, java.lang.String)
The clue is the last parameter, the first two are correct. However, I would've expected null as the value instead of a completely different type.
I'm going through the "Getting Started with Grails" ebook and have hit a wall with chapter 4 (Validation) on page 38 (actual page 50). Here is the code:
Oh, there might be a typo in the code in the book, though it didn't affect the behavior or error messages I got, on the following line:
def code = badField?.codes.find {
it == 'race.startDate.validator.invalid'
}
As I said, it doesn't affect the main execution, but was just curious if I'm right or if this is something in Groovy I haven't run across yet. I put what I thought it should be below.
package racetrack
import groovy.util.GroovyTestCase
class RaceIntegrationTests extends GroovyTestCase {
void testRaceDatesBeforeToday() {
def lastWeek = new Date() - 7
def race = new Race(startDate:lastWeek)
assertFalse "Validation should not succeed", race.validate()
// It should have errors after validation fails
assertTrue "There should be errors", race.hasErrors()
println "\nErrors:"
println race.errors ?: "no errors found"
def badField = race.errors.getFieldError('startDate')
println "\nBadField:"
println badField ?: "startDate wasn't a bad field"
assertNotNull "Expecting to find an error on the startDate field", badField
def code = badField ?: codes.find {
it == 'race.startDate.validator.invalid'
}
println "\nCode:"
println code ?:"the custom validator for startDate wasn't found"
assertNotNull "startDate field should be the culprit", code
}
}
where, when running "grails test-app", I get the following:
Error executing script TestApp: java.lang.RuntimeException: Could not load class in test type 'integration'
java.lang.RuntimeException: Could not load class in test type 'integration'
at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:391)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:427)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:415)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.executeTargets(Gant.groovy:590)
at gant.Gant.executeTargets(Gant.groovy:589)
Caused by: java.lang.RuntimeException: Could not load class in test type 'integration'
at _GrailsTest_groovy$_run_closure4.doCall(_GrailsTest_groovy:261)
at _GrailsTest_groovy$_run_closure4.call(_GrailsTest_groovy)
at _GrailsTest_groovy$_run_closure2.doCall(_GrailsTest_groovy:228)
at _GrailsTest_groovy$_run_closure1_closure21.doCall(_GrailsTest_groovy:187)
at _GrailsTest_groovy$_run_closure1.doCall(_GrailsTest_groovy:174)
at TestApp$_run_closure1.doCall(TestApp.groovy:82)
at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
... 10 more
The book is using Grails 1.2.x and I'm using 1.3.x and already noticed some discrepancies between the versions (nothing unsurmountable), so it could be something like that, but I can't seem to figure it out. Being new to Groovy and Grails isn't helping! :-D
Can anyone explain what I can do to get past this?
I just got this error, my cause was that my test class was in the wrong package.
I could not find a way to get a clearer description of the problem, even running with the --stacktrace option showed no more information.
It seems like this error can be caused by different compilation issues, perhaps?
I had the same problem (although I'm using Grails 2.3.4) - I fixed it by explicitly including
import racetrack.Race
instead of
package racetrack
Interestingly, after I tried this I commented it out and everything still worked - until I did a grails clean. Then it failed again. Suspect something not quite 100% in the grails / groovy auto compilation stuff.
I hit this problem with Grails 2.4.2. The cause was I had a test file named FooTest, but the class was named FooTest**s**.
Running grails test-app --stacktrace helped find the offending class.
First of all, I don't think you need this to be an 'integration' test. Place it under the 'src/test/unit/...' directory structure. Second of all, if you want to test the Grails 'validate()' method that is going to be injected by the Grails framework based on your 'constraints' block, you must make the test extend 'GrailsUnitTest' and call 'mockDomain(Race)' on the first line of your unit test method. If that is unclear, ping me and I'll post code but my guess is your book has a pretty good example of this. Here is some 'free hand' code that might fix it...
class RaceTests extends GrailsUnitTest {
void testRaceDatesBeforeToday() {
mockDomain(Race)
...
please make sure that your package name is correct, the above error means that its trying to run the test but since the package name is specified wrong its not able to find the file with that particular package name.