Programatically call Spock Tests to create a Load Test

陌路散爱 提交于 2020-07-23 06:22:21

问题


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.


回答1:


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 find, 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 havwe 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).



来源:https://stackoverflow.com/questions/62827352/programatically-call-spock-tests-to-create-a-load-test

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!