Skip to content

Instantly share code, notes, and snippets.

@jweden
Created April 7, 2013 02:13
Show Gist options
  • Save jweden/5328575 to your computer and use it in GitHub Desktop.
Save jweden/5328575 to your computer and use it in GitHub Desktop.
/**
* Example Pattern to use to quickly create continuous Performance tests along side Functional Acceptance Tests for REST APIs
*/
package example
import static org.junit.Assert.assertEquals
import org.junit.Test
/**
* Always good to have a parent class for test plans to centralize code across all test plans
*/
abstract class AbstractTestPlan {
protected PersonClient personClient
AbstractTestPlan() {
personClient = new PersonClient()
}
}
class PerformanceTestPlan extends AbstractTestPlan{
def maxSecondsToWaitForAllResults = 60
def concurrencyLevel = 1000
/**
* Send in concurrent requests and validate only correct response code received for all responses
*/
@Test
void getPersonPerf() throws Exception {
// Notice no overhead for unmarshalling response payload to reduce cpu and memory overhead
personClient.getPerson(concurrencyLevel, HTTPResponseCodes.OK, maxSecondsToWaitForAllResults)
}
}
class FunctionalTestPlan extends AbstractTestPlan {
def maxSecondsToWaitForAllResults = 2
def concurrencyLevel = 1
/**
* Send in a single GET Person request and validate correct response code and payload
*/
@Test
void getPerson() throws Exception {
def mockExpectedPerson = new Person();
def personReceived = personClient.getPersonAndUnmarshall(concurrencyLevel, HTTPResponseCodes.OK, maxSecondsToWaitForAllResults)
assertEquals("Validating Person received from GET request is the one expected",mockExpectedPerson, personReceived)
}
}
/**
* The Person domain object (DTO)
*/
class Person {
//Imagine code for fields such as name, address etc.
}
enum HTTPResponseCodes {
OK("200"),
BAD_REQUEST("404"),
//and all the other response codes used in tests too
}
/**
* Contains logic to send concurrent (ideally non-blocking) HTTP requests and receive responses. This class could call into other classes
* to generate performance statistics (average response time, average requests/second, etc.) across all the requests
* and, in turn, store these in a database for reporting and trending.
*/
abstract class RestClient {
List<String> doGet (long concurrencyLevel, String url, HTTPResponseCodes expectedResponseCode, long maxSecondsToWaitForAllResults) {
//Imagine code to send http requests to specified url and return responses. Imagine a loop waiting for up to maxSecondsToWaitForAllResults for responses to finish
String mockResponse = "some response returned from http get";
def mockResponseCode = HTTPResponseCodes.OK
//Do response code assert here so that we aren't duplicating response code asserts in tests -- DRY Principle
assertEquals("Validating response code", expectedResponseCode, mockResponseCode)
// Optionally store performance statistics in a database for trending purposes (not shown)
return [mockResponse]
}
}
/**
* Holds the business logic for requests to send and responses to receive. This is what is called from the test plan classes.
*/
class PersonClient extends RestClient{
private static final String PERSON_URL = "https://get/person"
private List<Person>unmarshallResponse(List<String> stringResponses) {
//Imagine code to unmarshall string responses into List of Persons
def mockPersonResponse = new Person()
return [mockPersonResponse];
}
def List<Person> getPersonAndUnmarshall(long concurrencyLevel, HTTPResponseCodes expectedResponseCode, long maxSecondsToWaitForAllResults) {
List<String> stringResponses = doGet(concurrencyLevel, PERSON_URL, expectedResponseCode, maxSecondsToWaitForAllResults)
return unmarshallResponse(stringResponses)
}
def List<String> getPerson(long concurrencyLevel, HTTPResponseCodes expectedResponseCode, long maxSecondsToWaitForAllResults) {
return doGet(concurrencyLevel, PERSON_URL, expectedResponseCode, maxSecondsToWaitForAllResults)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment