Skip to content

Groovy Script Structure

juno.yoon edited this page May 22, 2016 · 2 revisions

Not like Jython Script Structure, Groovy Script Structure is based on JUnit. It’s because we want to reuse existing JUnit experiences as much as possible. Several IDEs already well integrate JUnit and let a user to run the test case directly. If you created Goorvy Maven Project in script creation dialog box in nGrinder, you can enjoy the this excellent approach in your current IDE.

For now, I’d like to explain the basic Groovy script structure. This is applied whether you choose Groovy Script or Groovy Maven Project.

Following is a base template for groovy script.

import static net.grinder.script.Grinder.grinder
import static org.junit.Assert.*
import static org.hamcrest.Matchers.*
import net.grinder.plugin.http.HTTPRequest
import net.grinder.script.GTest
import net.grinder.script.Grinder
import net.grinder.scriptengine.groovy.junit.GrinderRunner
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess

import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith

import HTTPClient.HTTPResponse

/**
 * A simple example using the HTTP plugin that shows the retrieval of a
 * single page via HTTP.
 *
 * This script is auto generated by ngrinder.
 *
 * @author ${userName}
 */
@RunWith(GrinderRunner)
class Test1 {

    public static GTest test;
    public static HTTPRequest request;

    // This method is executed once per a process.
    @BeforeProcess
    public static void beforeClass() {
        test = new GTest(1, "${name}");
        request = new HTTPRequest();
        test.record(request);
        grinder.logger.info("before process.");
    }

    // This method is executed once per a thread.
    @BeforeThread
    public void beforeThread() {
        grinder.statistics.delayReports=true;
        grinder.logger.info("before thread.");
    }

    // This method is continuously executed until you stop the test
    @Test
    public void test(){
        HTTPResponse result = request.GET("${url}");
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
        } else {
            assertThat(result.statusCode, is(200));
        }
    }
}

The Groovy test case in nGrinder should be annotated with @RunWith(GroovyRunner). When you run this script in JUnit Runner of each IDE, this part makes GroovyRunner to control the JUnit behavior and mount the grinder context on JUnit.

@Test annotated methods are subject to be executed repeatedly. As you can see above, you can assert the test result using JUnit assertion. If the assertion is failed, the last test bounded to this thread will be evaluated as a failure.

Instead of @BeforeClass @Before @AfterClass @After annotations which are used in the generic JUnit tests, Groovy test case in nGrinder uses its own annotation to control script behaviors.

Annotation Description Applied to Usage
@BeforeProcess Define behaviors which should be executed before the process is invoked static method Load resource files which are shared by threads. Define GTest and instrument the test target using record method.
@AfterProcess Define behaviors which should be executed after the process is terminated static method Close resource files.
@BeforeThread Define behaviors which should be executed before the each thread is executed. member method Login the target system. Set up the thread bounded values. (For example, Cookie Handler)
@AfterThread Define behaviors which should be exeucted after the each thread is exeucted member method Logout the target system
@Before Define behaviors which should be executed before every @Test annotated method are executed. member method Common logic which is shared by the multiple @Test annotated methods. Variable set up
@After Define behaviors which should be executed after every @Test annotated methods are executed. member method Not used much..
@Test Define the test behavior. Excuted muliple times member method Test body

The execution flow can be illustrated by following.

In the test case sample, Please look below part carefully.

public static GTest test;
public static HTTPRequest request;

// This method is executed once per a process.
@BeforeProcess
public static void beforeProcess() {
    // Instead of Test in Jython, GTest is used here
    // It's because the identifier "Test" is alredy used by the @Test
    // GTest is the replacement to avoid the naming confliction.
    test = new GTest(1, "test name");
    request = new HTTPRequest();
    test.record(request);
    grinder.logger.info("before process.");
}

In @BeforeProcess annotated method, it defines GTest instance and records the HTTPRequest instance to sense any calls on HTTPRequest instance and save it in the static member variable(request).
It’s the first step of test statistic preparation. Any method calls on request variable will increase the TPS. If you want to record multiple HTTPRequest instances using multiple GTest named differently, You can just add GTest instance in another member variables and invoke record method on different HTTPRequest instance.

You can define the multiple test methods in a single test case class as well. Just add @Test annotation on those like the following.

@Test
public void testGoogle(){
    HTTPResponse result = request.GET("http://www.google.com");
    if (result.statusCode == 301 || result.statusCode == 302) {
        grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
    } else {
        assertThat(result.statusCode, is(200));
    }
}

@Test
public void testYahoo(){
    HTTPResponse result = request.GET("http://www.yahoo.com");
    if (result.statusCode == 301 || result.statusCode == 302) {
        grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
    } else {
        assertThat(result.statusCode, is(200));
    }
}

However, you should aware nGrinder GroovyRunner creates only one test case object per a thread not like that generic JUnit test cases which creates a separate test case object for each @Test annotated method. So if each @Test annotated methods share member variables, they can affect each other. It’s intended. In nGrinder test case, it’s very likely each @Test method relies on the previous @Test annotated method execution result. Therefore, if you save the previous test result in the member variable and refer it to decide to continue to next test method, you can easily implement the test dependency.

private boolean googleResult;

@Test
public void testGoogle(){
    googleResult = false;
    HTTPResponse result = request.GET("http://www.google.com");
    if (result.statusCode == 301 || result.statusCode == 302) {
        grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
    } else {
        assertThat(result.statusCode, is(200));
    }
    googleResult = true;
}

@Test
public void testYahoo(){
    if (!googleResult) {
        grinder.logger.warn("Just return. Because prev google test is failed.");
        return;
    }
    HTTPResponse result = request.GET("http://www.yahoo.com");
    if (result.statusCode == 301 || result.statusCode == 302) {
        grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
    } else {
        assertThat(result.statusCode, is(200));
    }
}

How can you use library and resources? If you don’t use Groovy Maven Structure yet, you can refer Jython Script Structure to how to include them in your script. It’s exactly same as Jython way. However if you use Maven structure plus Groovy, you can enjoy the easier library dependency setting and run the JUnit test in your IDE. Interested in this? Then continue to see the Groovy Maven Structure to know how to enable this for you.

Clone this wiki locally