Development
Find here information on how to configure applications, different supported databases, spring boot integration, and the CDS Maven Plugin.
Content
Application Configuration
This section describes how to configure applications. CAP Java applications can fully leverage Spring Boot’s capabilities for Externalized Configuration. This enables you to define multiple configuration profiles for different scenarios, like local development and cloud deployment.
For a first introduction, have a look at our sample application and the configuration profiles we added there.
Now, that you’re familiar with how to configure your application, start to create your own application configuration. See the full list of CDS properties as a reference.
SAP HANA
SAP HANA can be configured when running locally as well as when running productively in the cloud. The datasource is auto-configured based on available service bindings in the VCAP_SERVICES
environment variable or locally the default-env.json. This only works if an application profile is used, that doesn’t explicitly configure a datasource using spring.datasource.url
. Such an explicit configuration always takes precedence over service bindings from the environment.
Service bindings of type instance-manager (deprecated), service-manager and, in a Spring-based application, hana are used to auto-configure datasources. If multiple datasources are used by the application, you can select one auto-configured datasource to be used by the Persistence Service through the property cds.dataSource.binding
.
Datasource Configuration
Datasources created from a service binding can be configured through the properties cds.dataSource.<service-instance>.<pool-type>.*
. An example configuration could look like this:
cds:
dataSource:
my-service-instance:
hikari:
maximum-pool-size: 20
Supported pool types for single tenant scenarios are hikari
, tomcat
, and dbcp2
. For a multitenant scenario hikari
, tomcat
, and atomikos
are supported. The corresponding pool dependencies need to be available on the classpath.
Datasources in Spring
By default no datasource is marked as the primary datasource bean. If the property cds.dataSource.binding
is set, the datasource bean created from this service-instance is marked as primary. In case, there are multiple datasources registered it’s required to define one datasource as primary. The primary datasource bean is the one that is used by the default Persistence Service.
H2
For local development, H2 can be configured to run in-memory or in the file-based mode. To generate SQL for H2, the sql dialect in .cdsrc.json has to be set to “plain”:
"sql": {
"dialect": "plain"
}
In Spring, H2 is automatically initialized in-memory when present on the classpath. See the official documentation for H2 for file-based database configuration.
SQLite
Also SQLite can be used for local development using the sqlite dialect in .cdsrc.json:
"sql": {
"dialect": "sqlite"
}
File-Based Storage
The database content is stored in a file, sqlite.db
as in the following example. Since the schema is initialized using cds deploy
command, the initialization mode is set to never
:
---
spring:
profiles: sqlite
datasource:
url: "jdbc:sqlite:sqlite.db"
driver-class-name: org.sqlite.JDBC
initialization-mode: never
hikari:
maximum-pool-size: 1
In-Memory Storage
The database content is stored in-memory only. The schema initialization done by Spring, executes the schema.sql
script. Hence, the initialization mode is set to always
. If Hikari closes the last connection from the pool, the in-memory database is automatically deleted. To prevent this situation, set max-lifetime
to 0:
---
spring:
profiles: default
datasource:
url: "jdbc:sqlite:file::memory:?cache=shared"
driver-class-name: org.sqlite.JDBC
initialization-mode: always
hikari:
maximum-pool-size: 1
max-lifetime: 0
Service Bindings on SAP BTP, Kyma Runtime
In the SAP BTP, Kyma Runtime, credentials of service bindings are stored in Kubernetes secrets. Using volumes, you can mount secrets into your application’s container. These volumes contain a file for each of the secrets properties.
Get the Secret into Your Container
To use a Kubernetes secret with your CAP service, you create a volume from it and mount it to the service’s container.
For example:
spec:
volumes:
- name: bookshop-db-secret-vol
secret:
secretName: bookshop-db-secret
containers:
- name: app-srv
...
volumeMounts:
- name: bookshop-db-secret-vol
mountPath: /etc/secrets/sapcp/hana/bookshop-db
readOnly: true
Prepare Your CAP Application
Add the cds-feature-k8s
feature in the pom.xml file of your CAP application to consume service credentials:
<dependencies>
<!-- Features -->
<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-feature-k8s</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
The feature supports reading multiple credentials from a common base directory and to read credentials from arbitrary directories.
Read Credentials from a Base Directory
The base directory for service credentials is the /etc/secrets/sapcp directory. You can overwrite the default base directory with the cds.environment.k8s.secretsPath
property.
Within this base directory, the directory structure for the service credentials is <service-name>/<instance-name>.
Read Credentials from Arbitrary Directories
You can also configure service bindings using the cds.environment.k8s.serviceBindings
configuration property.
For example:
cds:
environment:
k8s:
serviceBindings:
bookshop-db:
secretsPath: /etc/secrets/hana
service: hana
plan: hdi-shared
tags:
- hana
- db
bookshop-uaa:
secretsPath: /etc/somewhere/else/xsuaa
...
The parameters plan
and tags
are optional.
Database Support in Java
This section describes the different databases, which are supported by CAP Java SDK and any differences between them with respect to CAP features. There’s out of the box support for SAP HANA with CAP currently as well as SQLite. However, it’s important to note that SQLite isn’t an enterprise grade database and is recommended for nonproductive use like local development or CI tests only.
SAP HANA
SAP HANA is supported as the CAP standard database and recommended for productive use with needs for schema evolution and multitenancy. Some salient points to note with SAP HANA are:
- Views are supported as described in Updatable Views, else any operation on views are defaulted to SAP HANA, which has limitations as described in the documentation.
PostgreSQL
CAP doesn’t support PostgreSQL out of the box. However, CAP Java SDK is tested on PostgreSQL 12 and supports in general most of the features of CAP. Nevertheless, find here a list of unsupported features/differences:
- CAP doesn’t support PostgreSQL specific schema generation.
- There’s no support for Schema Evolution for PostgreSQL.
- Localization is supported just as in SQLite:
- PostgreSQL doesn’t support the
SESSION_CONTEXT
method to dynamically determine the locale from a connection. Instead,cds
creates two SQL views for the languages German and French by default. - Set the CDS property
cds.sql.supportedLocales
with a comma-separated list. Add all language codes defined for the data model to inform the CAP Java runtime to support these additional locales. - This limitation can be overcome by preserving the association
localized
in view definitions. Then the CDS runtime can take over and modify a query to be fully localization aware. - Learn more about resolution of localized data at runtime and via views.
- PostgreSQL doesn’t support the
- The sort order of queries behaves as configured on the database. Locale specific sorting isn’t supported.
- Currently, PostgreSQL only supports As-of-now queries with temporal data.
- Views are updatable as long as they’re supported by the CAP Java SDK, else they default to PostgreSQL, where updatable views have limitations.
SQLite
CAP supports SQLite out of the box. When working with Java, it’s recommended to use SQLite only for development and testing purposes. There’s no production support for SQLite from CAP.
CAP does support most of the major features on SQLite, although there are a few shortcomings that are listed here:
-
Localization is supported for all locales. However, you need to set the CDS property
cds.sql.supportedLocales
with a comma-separated list. Add all language codes defined for the data model. Also ensure that the corresponding convenience views are generated by setting the configuration as shown in the Localized Data guide.-
This limitation can be avoided by avoiding views that have a closed/fixed select list. Once a given CQL query uses * and excluding like this
SELECT from Foo {*} excluding {bar}
the CDS runtime can take over and modify a query to be fully localization aware. -
Learn more about resolution of localized data at runtime and via views.
-
- Any views generated for SQLite are read-only by default, inserting records can only happen with a one to one projection.
RIGHT
andFULL OUTER JOIN
isn’t supported.- There are some known issues with parentheses in
UNION
operator. The following statement is erroneous:SELECT * FROM A UNION ( SELECT * FROM B )
. Instead, use:SELECT * FROM A UNION SELECT * FROM B
without parentheses. This can be achieved by removing the parentheses in your CDS Model. - SQLite has only limited support for concurrent database access. You’re advised to limit the connection pool to 1 as shown above (parameter
maximum-pool-size: 1
), which effectively serializes all database transactions. - The predicate function
contains
is supported. However, the search for characters in the word or phrase is case-insensitive in SQLite. In the future, we might provide an option to make the case-sensitivity locale dependent. - SQLite doesn’t support pessimistic locking (
SELECT FOR UPDATE
) on the query level. Thus, to avoidSQLException
at runtime, the generated SQL statements, passed to a database, don’t containFOR UPDATE
clause. Nevertheless, the data consistency can be guaranteed by the fact, that only one process can be making changes to the database at any moment in time. - Streaming of large object data isn’t supported by SQLite. Hence, when reading or writing data of type
cds.LargeString
andcds.LargeBinary
as a stream the framework temporarily materializes the content. Thus, storing large objects on SQLite can impact the performance. - Sorting of character-based columns is never locale-specific but if any locale is specified in the context of a query then case insensitive sorting is performed.
- Currently, SQLite only supports as-of-now queries with temporal data.
- Views in SQLite are read-only. However, the CAP Java SDK supports some views to be updatable as described here.
- Foreign key constraints are supported, but disabled by default. To activate the feature using JDBC URL, append the
foreign_keys=on
parameter to the connection URL, for example,url=jdbc:sqlite:file:testDb?mode=memory&foreign_keys=on
. For more information, visit the SQLite Foreign Key Support in the official documentation.
H2 Database
H2 is one of the recommended in-memory databases for local development. There’s no production support for H2 from CAP and there are the following support limitations:
- Localization is supported for all locales. Unlike SAP HANA, H2 doesn’t support to set the locale for a connection.
- As in SQLite, set a CDS property
cds.sql.supportedLocales
with a comma-separated list. Add all language codes defined for the data model. - This limitation can be avoided by avoiding views that have a closed /fixed select list. Once a given CQL query uses * and excluding like this
SELECT from Foo {*} excluding {bar}
the CDS runtime can take over and modify a query to be fully localization aware. - Learn more about resolution of localized data at runtime and via views.
- As in SQLite, set a CDS property
- H2 only supports database level collation. Lexicographical sorting on character-based columns isn’t supported.
- Case-insensitive comparison isn’t yet supported.
- Currently, only as-of-now queries with temporal data are supported.
- By default, views aren’t updatable on H2. However, the CAP Java SDK supports some views to be updatable as described here.
- For correct order of views in the generated
schema.sql
file CDS Compiler v2 must be used. - Although referential and foreign key constraints are supported, nevertheless H2 doesn’t support deferred checking. As a consequence, schema SQL is never generated with referential constraints.
Spring Boot Integration
This section describes the Spring Boot integration of the CAP Java SDK. Classic Spring isn’t supported. Running your application with Spring Boot framework offers a number of helpful benefits that simplify the development and maintenance of the application to a high extend. Spring not only provides a rich set of libraries and tools for most common challenges in development, you also profit from a huge community, which constantly contributes optimizations, bug fixes and new features.
As Spring Boot not only is widely accepted but also most popular application framework, CAP Java SDK comes with a seamless integration of Spring Boot as described in the following sections.
Spring Dependencies
To make your web application ready for Spring Boot, you need to make sure that the following Spring dependencies are referenced in your pom.xml
(group ID org.springframework.boot
):
spring-boot-starter-web
spring-boot-starter-jdbc
spring-boot-starter-security
(optional)
In addition, for activating the Spring integration of CAP Java SDK, the following runtime dependency is required:
<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-framework-spring-boot</artifactId>
<version>${revision}</version>
<scope>runtime</scope>
/dependency>
It might be more convenient to make use of CDS starter bundle cds-starter-spring-boot-odata
, which not only comprises the necessary Spring dependencies, but also configures the OData V4 protocol adapter:
<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-starter-spring-boot-odata</artifactId>
<version>${revision}</version>
</dependency>
Spring Features
Beside the common Spring features such as dependency injection and a sophisticated test framework, the following features are available in Spring CAP applications in addition:
- CDS event handlers within custom Spring beans are automatically registered at startup.
- Full integration into Spring transaction management (
@Transactional
is supported). - A various number of CAP Java SDK interfaces are exposed as Spring beans and are available in Spring application context such as technical services, the
CdsModel
or theUserInfo
in current request scope. - Automatic configuration of XSUAA, IAS and mock user authentication by means of Spring security configuration.
- Integration of
cds
-property section into Spring properties. See section Externalized Configuration in the Spring Boot documentation for more details. - cds actuator exposing monitoring information about CDS runtime and security.
- DB health check indicator which also applies to tenant-aware DB connections.
None of the listed features will be available out of the box in case you choose to pack and deploy your web application as plain Java Servlet in a war file.
Spring Beans Exposed by the Runtime
Bean | Description | Example |
---|---|---|
CdsRuntime | Runtime instance (singleton) | @Autowired CdsRuntime runtime; |
CdsRuntimeConfigurer | Runtime configuration instance (singleton) | @Autowired CdsRuntimeConfigurer configurer; |
Service | All kinds of CDS services, application services and technical services | @Autowired @Qualifier(CatalogService_.CDS_NAME) private ApplicationService cs; @Autowired private PersistenceService ps; |
ServiceCatalog | The catalog of all available services | @Autowired CatalogService catalog; |
CdsModel | The current model | @Autowired CdsModel model; |
UserInfo | Information about the authenticated user | @Autowired UserInfo userInfo; |
AuthenticationInfo | Authentication claims | @Autowired AuthenticationInfo authInfo; |
ParameterInfo | Information about request parameters | @Autowired ParameterInfo paramInfo; |
Messages | Interface to write messages | @Autowired Messages messages; |
FeatureTogglesInfo | Information about feature toggles | @Autowired FeatureTogglesInfo ftsInfo; |
CdsDataStore | Direct access to default data store | @Autowired CdsDataStore ds; |
Building CAP Java Applications
This section describes various options to create a CAP Java project from scratch, to build your application with Maven, and to modify an existing project with the CDS Maven plugin.
The Maven Archetype
Use the following command line to create a project from scratch with the CDS Maven archetype:
mvn archetype:generate -DarchetypeArtifactId=cds-services-archetype -DarchetypeGroupId=com.sap.cds -DarchetypeVersion=RELEASE
mvn archetype:generate -DarchetypeArtifactId=cds-services-archetype -DarchetypeGroupId=com.sap.cds -DarchetypeVersion=RELEASE
mvn archetype:generate `-DarchetypeArtifactId=cds-services-archetype `-DarchetypeGroupId=com.sap.cds `-DarchetypeVersion=RELEASE
It supports the following command line options:
Option | Description |
---|---|
-DincludeModel=true |
Adds a minimalistic sample CDS model to the project |
-DincludeIntegrationTest=true |
Adds an integration test module to the project |
-DodataVersion=[v2|v4] |
Specify which protocol adapter is activated by default |
Maven Build Options
You can build and run your application by means of the following Maven command:
mvn spring-boot:run
The following sections describe additional options you can apply during the build.
Project-Specific Configuration in .cdsrc.json
If you can’t stick to defaults, you can use the .cdsrc.json to add specific configuration to your project. The configuration is used by the build process using @sap/cds-dk
.
Learn more about configuration and cds.env
Using a specific cds-dk version
By default, the build is configured to download a Node.js runtime and the @sap/cds-dk
tools and install them locally within the project.
To keep applications up-to-date the latest version of the current major release of @sap/cds-dk
is installed by default.
If you want to ensure that the build is fully reproducible you need to fix the version of @sap/cds-dk
.
You can achieve this by adding the following property to the properties
section in your pom.xml
:
<properties>
...
<cds.install-cdsdk.version>REPLACE WITH FIXED VERSION</cds.install-cdsdk.version>
</properties>
Make sure to regularly update @sap/cds-dk
according to our guidance, if you are using a fixed version.
Using a Global cds-dk
By default, the build is configured to download a Node.js runtime and the @sap/cds-dk
tools and install them locally within the project.
This step makes the build self-contained, but the build also takes more time. You can omit these steps and speed up the Maven build, using the Maven profile cdsdk-global
.
Prerequisites:
@sap/cds-dk
is globally installed.- Node.js installation is available in current PATH environment.
If these prerequisites are met, you can use the profile cdsdk-global
by executing:
mvn spring-boot:run -P cdsdk-global
Refreshing the Local cds-dk
By default, the goal install-cdsdk
of the cds-maven-plugin
skips the installation of the @sap/cds-dk
, if the @sap/cds-dk
is already installed. To update the @sap/cds-dk
version in your application project do the following:
- Specify a newer version of
@sap/cds-dk
in your pom.xml file. -
Execute
mvn spring-boot:run
with an additional property-Dcds.install-cdsdk.force=true
, to force the installation of a@sap/cds-dk
in the configured version.mvn spring-boot:run -Dcds.install-cdsdk.force=true
Recommendation
This should be done at least with every major update of @sap/cds-dk
.
CDS Maven Plugin
CDS Maven plugin provides several goals to perform CDS-related build steps. It can be used in CAP Java projects to perform the following build tasks:
- Install Node.js in the specified version
- Install the CDS Development Kit
@sap/cds-dk
in a specified version - Perform arbitrary CDS commands on a CAP Java project
- Generate Java classes for type-safe access
- Clean a CAP Java project from artifacts of the previous build
Since CAP Java 1.7.0, that CDS Maven Archetype sets up projects to leverage the CDS Maven plugin to perform the previous mentioned build tasks. On how to modify a project generated with a previous version of the CDS Maven Archetype, see this commit.
See CDS Maven Plugin documentation for more details.
Testing CAP Java Applications
This section describes some best practices and recommendations for testing CAP Java applications.
As described in Modular Architecture, a CAP Java application consists of weakly coupled components, which enables you to define your test scope precisely and focus on parts that need a high test coverage.
Typical areas that require testing are the services that dispatch events to event handlers, the event handlers themselves that implement the behaviour of the services, and finally the APIs that the application services define and that are exposed to clients through OData.
Aside from JUnit, the Spring framework provides much convenience for both unit and integration testing, like dependency injection via autowiring or the usage of MockMvc and mocked users. So whenever possible, it is recommended to utilize it for writing tests.
Best Practices
To illustrate this, the following examples demonstrate some of the recommended ways of testing. All the examples are taken from the CAP Java bookshop sample project in a simplified form, so definitely have a look at this as well.
Let’s assume you want to test the following custom event handler:
@Component
@ServiceName(CatalogService_.CDS_NAME)
public class CatalogServiceHandler implements EventHandler {
private final PersistenceService db;
public CatalogServiceHandler(PersistenceService db) {
this.db = db;
}
@On
public void onSubmitOrder(SubmitOrderContext context) {
Integer quantity = context.getQuantity();
String bookId = context.getBook();
Optional<Books> book = db.run(Select.from(BOOKS).columns(Books_::stock).byId(bookId)).first(Books.class);
book.orElseThrow(() -> new ServiceException(ErrorStatuses.NOT_FOUND, MessageKeys.BOOK_MISSING)
.messageTarget(Books_.class, b -> b.ID()));
int stock = book.map(Books::getStock).get();
if (stock >= quantity) {
db.run(Update.entity(BOOKS).byId(bookId).data(Books.STOCK, stock -= quantity));
SubmitOrderContext.ReturnType result = SubmitOrderContext.ReturnType.create();
result.setStock(stock);
context.setResult(result);
} else {
throw new ServiceException(ErrorStatuses.CONFLICT, MessageKeys.ORDER_EXCEEDS_STOCK, quantity);
}
}
@After(event = CqnService.EVENT_READ)
public void discountBooks(Stream<Books> books) {
books.filter(b -> b.getTitle() != null).forEach(b -> {
loadStockIfNotSet(b);
discountBooksWithMoreThan111Stock(b);
});
}
private void discountBooksWithMoreThan111Stock(Books b) {
if (b.getStock() != null && b.getStock() > 111) {
b.setTitle(String.format("%s -- 11%% discount", b.getTitle()));
}
}
private void loadStockIfNotSet(Books b) {
if (b.getId() != null && b.getStock() == null) {
b.setStock(db.run(Select.from(BOOKS).byId(b.getId()).columns(Books_::stock)).single(Books.class).getStock());
}
}
}
You can find a more complete sample of the previous snippet in our CAP Java bookshop sample project.
The CatalogServiceHandler
here implements two handler methods – onSubmitOrder
and discountBooks
– that should be covered by tests.
The method onSubmitOrder
is registered to the On
phase of a SubmitOrder
event and basically makes sure to reduce the stock quantity of the ordered book by the order quantity, or, in case the order quantity exceeds the stock, throws a ServiceException
.
Whereas discountBooks
is registered to the After
phase of a read
event on the Books
entity and applies a discount information to a book’s title if the stock quantity is larger than 111.
Event Handler Layer Testing
Out of these two handler methods discountBooks
does not actually depend on the PersistenceService
.
That allows us to verify its behavior in a unit test by creating a CatalogServiceHandler
instance with the help of a PersistenceService
mock to invoke the handler method on, as demonstrated below:
For mocking, you can use Mockito, which is already included with the spring-boot-starter-test
“Starter”.
@ExtendWith(MockitoExtension.class)
public class CatalogServiceHandlerTest {
@Mock
private PersistenceService db;
@Test
public void discountBooks() {
Books book1 = Books.create();
book1.setTitle("Book 1");
book1.setStock(10);
Books book2 = Books.create();
book2.setTitle("Book 2");
book2.setStock(200);
CatalogServiceHandler handler = new CatalogServiceHandler(db);
handler.discountBooks(Stream.of(book1, book2));
assertEquals("Book 1", book1.getTitle(), "Book 1 was discounted");
assertEquals("Book 2 -- 11% discount", book2.getTitle(), "Book 2 was not discounted");
}
}
You can find a variant of this sample code also in our CAP Java bookshop sample project.
Whenever possible, mocking dependencies and just testing the pure processing logic of an implementation allows you to ignore the integration bits and parts of an event handler, which is a solid first layer of your testing efforts.
Service Layer Testing
Application Services that are backed by an actual service definition within the CdsModel
implement an interface, which extends the Service
interface and offers a common CQN execution API
for CRUD
events. This API can be used to run CQN
statements directly against the service layer, which can be used for testing, too.
To verify the proper discount application in our example, we can run a Select
statement against the CatalogService
and assert the result as follows, using a well-known dataset:
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class CatalogServiceTest {
@Autowired
@Qualifier(CatalogService_.CDS_NAME)
private CqnService catalogService;
@Test
public void discountApplied() {
Result result = catalogService.run(Select.from(Books_.class).byId("51061ce3-ddde-4d70-a2dc-6314afbcc73e"));
// book with title "The Raven" and a stock quantity of > 111
Books book = result.single(Books.class);
assertEquals("The Raven -- 11% discount", book.getTitle(), "Book was not discounted");
}
}
As every service in CAP implements the Service interface with its emit(EventContext) method, another way of testing an event handler is to dispatch an event context via the emit()
method to trigger the execution of a specific handler method.
Looking at the onSubmitOrder
method from our example above we see that it uses an event context called SubmitOrderContext
. Therefore, using an instance of that event context, in order to test the proper stock reduction, we can trigger the method execution and assert the result, as demonstrated:
@SpringBootTest
public class CatalogServiceTest {
@Autowired
@Qualifier(CatalogService_.CDS_NAME)
private CqnService catalogService;
@Test
public void submitOrder() {
SubmitOrderContext context = SubmitOrderContext.create();
// ID of a book known to have a stock quantity of 22
context.setBook("4a519e61-3c3a-4bd9-ab12-d7e0c5329933");
context.setQuantity(2);
catalogService.emit(context);
assertEquals(22 - context.getQuantity(), context.getResult().getStock());
}
}
The same way you can verify the ServiceException
being thrown in case of the order quantity exceeding the stock value:
@SpringBootTest
public class CatalogServiceTest {
@Autowired
@Qualifier(CatalogService_.CDS_NAME)
private CqnService catalogService;
@Test
public void submitOrderExceedingStock() {
SubmitOrderContext context = SubmitOrderContext.create();
// ID of a book known to have a stock quantity of 22
context.setBook("4a519e61-3c3a-4bd9-ab12-d7e0c5329933");
context.setQuantity(30);
catalogService.emit(context);
assertThrows(ServiceException.class, () -> catalogService.emit(context), context.getQuantity() + " exceeds stock for book");
}
}
For a more extensive version of the previous CatalogServiceTest
snippets, have a look at our CAP Java bookshop sample project.
API Integration Testing
Integration tests enable us to verify the behavior of a custom event handler execution doing a roundtrip starting at the protocol adapter layer and going through the whole CAP architecture until it reaches the service and event handler layer and then back again through the protocol adapter.
As the services defined in our CDS model
are exposed as OData
endpoints, by using MockMvc we can simply invoke a specific OData
request and assert the response from the addressed service.
The following demonstrates this by invoking a GET
request to the OData
endpoint of our Books
entity, which triggers the execution of the discountBooks
method of the CatalogServiceHandler
in our example:
@SpringBootTest
@AutoConfigureMockMvc
public class CatalogServiceITest {
private static final String booksURI = "/api/browse/Books";
@Autowired
private MockMvc mockMvc;
@Test
public void discountApplied() throws Exception {
mockMvc.perform(get(booksURI + "?$filter=stock gt 200&top=1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.value[0].title").value(containsString("11% discount")));
}
@Test
public void discountNotApplied() throws Exception {
mockMvc.perform(get(booksURI + "?$filter=stock lt 100&top=1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.value[0].title").value(not(containsString("11% discount"))));
}
}
Check out the version in our CAP Java bookshop sample project for additional examples of integration testing.
CDS Properties
Learn more about CDS properties to configure the CAP Java SDK.