Class Repository
Prerequisite
From release 1.3.0, CodeSV provides functionality for defining class repositories containing predefined transactions. The main advantage of class repositories is the easy reuse of defined transactions between different tests and test classes in the same project. It also improves the combination of transactions and provides a single place to configure a set of transactions.
Basic Usage
The most simple way to define the class repository is to create a new class and create public methods with the @TransactionDefinition
annotation and provide a unique name of the transaction. The uniqueness of the name is important as it is used for calling the defined transaction in test method.
Bodies of methods should define the virtualized services using common fluent API from CodeSV.
Class Repository:
public class DefaultRepository { public static final String SERVICE_URL = "http://www.carepositories.com"; /** * Demo method. */ @TransactionDefinition(name = "simpleResponse") public void virtualizedSimpleDefaultResponse() { ServiceResult result = new ServiceResult(); result.setTxnName("simpleResponse"); forGet(SERVICE_URL + "/simpleDefaultService").doReturn( okMessage().withJsonBody(new Gson().toJson(result)) ); } @TransactionDefinition(name = "commonResponse") public void commonResponse() { ServiceResult result = new ServiceResult(); result.setTxnName("commonResponse"); forGet(SERVICE_URL + "/commonService").doReturn( okMessage().withJsonBody(new Gson().toJson(result)) ); } }
Once the class repository is created, it is required to declare a VirtualServerRule
and provide information about it to the TxnRepoStore
object using the @TransactionClassRepository
annotation. This annotation has the required field repoClasses
. This is a list of classes defining class repositories.
To be able to find the class repositories in the project, it is required to provide this object to the TxnRepoStore
like this:
@TransactionClassRepository(repoClasses = {DefaultRepository.class}) public class ClassRepositoryExample { @Rule public VirtualServerRule vs = new VirtualServerRule(); private TxnRepoStore store = new TxnRepoStoreBuilder().build(this);
After the VirtualServerRule
and TxnRepoStore
are successfully created and it is found the defined class repositories, we can use its object and method useTransaction(String txnName)
to load the transactions from class repositories by the names provided in the @TransactionDefinition
annotation.
Full Test Class:
@TransactionClassRepository(repoClasses = {DefaultRepository.class}) public class ClassRepositoryExample { @Rule public VirtualServerRule vs = new VirtualServerRule(); private TxnRepoStore store = new TxnRepoStoreBuilder().build(this); @Test public void simpleMultipleCallTest() throws Exception { String simpleTransactionName = "simpleResponse"; String commonTransactionName = "commonResponse"; store.useTransaction(simpleTransactionName); store.useTransaction(commonTransactionName); ServiceResult result = getResult("/simpleDefaultService"); assertEquals(simpleTransactionName, result.getTxnName()); result = getResult("/commonService"); assertEquals(commonTransactionName, result.getTxnName()); } private ServiceResult getResult(String path) throws IOException { HttpClient client = HttpClientBuilder.create().build(); HttpGet request = new HttpGet(DefaultRepository.SERVICE_URL + path); HttpResponse response = client.execute(request); String responseBody = EntityUtils.toString(response.getEntity()); return new Gson().fromJson(responseBody, ServiceResult.class); } }
Advanced usage
The previous example is a simple way to provide the most basic usage and does not provide a lot flexibility because we are calling the transactions by names only. To give more control and flexibility for calling transactions and combinations CodeSV provides additional optional parameters and functionality.
@VirtualServiceRepository
This annotation is optional for class repositories. This annotation provides additional control over repositories and you can give a unique name to the whole class repository. This helps in with the definition of multiple repositories with similar transactions that all share the same name in the repositories. For example, you can define QA and DEV repositories with the same transaction name but different virtualization.
@VirtualServiceRepository(serviceName = "QA service") public class QaRepository { }
Tags
To provide even more control over similar transaction definitions, it is possible to use tags in the @TransactionDefition
annotation. Using combinations of virtual service name, transaction name and tags you can create complex logic for specific tests without duplication of logic in each test. The annotation has the optional parameter tags
that is used for specifying the list of tags of the transaction.
@TransactionDefinition(name = "simpleResponse", tags = {"COMMON", "QA"}) public void virtualizedSimpleDefaultResponse() { } @TransactionDefinition(name = "anotherResponse", tags = "QA") public void qaResponse() { }
UseTransactionRule
To leverage additional information for class repositories and transactions, it is required to use a special object in your tests as the basic method useTransaction(String txnName)
is not enough. For advance usage, CodeSV provides the UseTransactionRule
object with fluent API for defining criteria for transactions. Afterwards the created object can be passed to the transaction store using the method store.useTransactionWithRule(UseTransactionRule rule)
to load transactions matching the created criteria to the virtual server.
Defined rules use AND
logic, so only transactions that match all defined criteria are loaded. Transaction name is an optional parameter and can be omitted if you want to load all transaction based on tags. If the transaction definition has more than one tag and you define a rule with just one tag that matches one of the tags in the list of tags then the transaction is loaded.
// Loads all "myTransaction" transactions that also contain tag "COMMON" and "QA" UseTransactionRule rule = UseTransactionRule.RuleBuilder.crossServiceBuilder() .forTransaction("myTransaction") .withTag("COMMON") .withTag("QA") .build(); store.useTransactionWithRule(rule);
// Loads all transaction in repositories from "QA service" that contain "QA" tag UseTransactionRule rule = new UseTransactionRule.RuleBuilder("QA service").withTag("QA").build(); store.useTransactionWithRule(rule);
For a complete example see: Repositories examples