Hybrid Testing
Introduction
You can easily test your CAP application using a local database and mock ups. But at some point, you're going to want to test with real cloud services. Of course, you can always deploy your application to the cloud.
With hybrid testing capabilities, you can stay in your local development environment and avoid the long turnaround times of cloud deployment, and you can selectively decide which services you want to use from the cloud.
Use the cds bind
command to connect your application to services on the cloud. Start your application with the hybrid
profile to use these service bindings. You can switch between local mock configuration and cloud service configuration by simply setting or omitting the profile parameter.
Bind to Cloud Services
Bind a Local Application to Services on Cloud Foundry
cds bind -2 my-hana:my-hana-key
cds bind -2 my-hana:my-hana-key
Binds your CAP application to the service key my-hana-key
of the service instance my-hana
, using your currently targeted Cloud Foundry space. The service instance my-hana
is a managed service. cds bind also supports Cloud Foundry user-provided services.
Learn how to binding to user-provided services on cloud foundry.
Output:
[bind] - Retrieving data from Cloud Foundry...
[bind] - Binding db to Cloud Foundry managed service my-hana:my-hana-key with kind hana.
[bind] - Saving bindings to .cdsrc-private.json in profile hybrid.
[bind] -
[bind] - TIP: Run with cloud bindings: cds watch --profile hybrid
[bind] - Retrieving data from Cloud Foundry...
[bind] - Binding db to Cloud Foundry managed service my-hana:my-hana-key with kind hana.
[bind] - Saving bindings to .cdsrc-private.json in profile hybrid.
[bind] -
[bind] - TIP: Run with cloud bindings: cds watch --profile hybrid
Note: The service key needs to be created beforehand as it is not created by default when using mta deployment.
TIP
You can omit :my-hana-key
here, because the key name is just the name of the instance with -key
added.
In many cases, CAP knows which CDS service and kind to use for a cloud service. Like in the previous example, the db
CDS service gets bound and set to the hana
kind, because the given service instance is of type hana
with plan hdi-shared
.
Learn how to bind to arbitrary cloud services.
The binding information is stored in the .cdsrc-private.json file of your project in the requires
section:
{
"requires": {
"[hybrid]": {
"db": {
"kind": "hana",
"binding": {
"type": "cf",
"apiEndpoint": "https://api.sap.hana.ondemand.com",
"org": "your-cf-org",
"space": "your-cf-space",
"instance": "my-hana",
"key": "my-hana-key",
"vcap": {
"label": "hana",
"plan": "hdi-shared"
},
"resolved": false
}
}
}
}
}
{
"requires": {
"[hybrid]": {
"db": {
"kind": "hana",
"binding": {
"type": "cf",
"apiEndpoint": "https://api.sap.hana.ondemand.com",
"org": "your-cf-org",
"space": "your-cf-space",
"instance": "my-hana",
"key": "my-hana-key",
"vcap": {
"label": "hana",
"plan": "hdi-shared"
},
"resolved": false
}
}
}
}
}
Bindings are assigned to the hybrid
profile by default.
Note that no credentials are saved. Only the information about where the credentials can be obtained is stored on your machine.
All cds bind
command line options
Bind a Local Application to User-Provided Services on Cloud Foundry
cds bind my-ups -2 my-user-provided-service
cds bind my-ups -2 my-user-provided-service
Binds your CAP application to the user provided service instance my-user-provided-service
, using your currently targeted Cloud Foundry space. The service name my-ups
is optional - it has to match the service name used in the CDS required
services configuration.
Output:
[bind] - Retrieving data from Cloud Foundry...
[bind] - Binding my-ups to Cloud Foundry user provided service my-user-provided-service.
[bind] - Saving bindings to .cdsrc-private.json in profile hybrid.
[bind] -
[bind] - TIP: Run with cloud bindings: cds watch --profile hybrid
[bind] - Retrieving data from Cloud Foundry...
[bind] - Binding my-ups to Cloud Foundry user provided service my-user-provided-service.
[bind] - Saving bindings to .cdsrc-private.json in profile hybrid.
[bind] -
[bind] - TIP: Run with cloud bindings: cds watch --profile hybrid
cds watch --profile hybrid
will automatically resolve user-provided service instance bindings using the same technique as for any other managed service binding.
Bind a Local Application to Services on Kubernetes
You can bind to Service Bindings of Open Service Broker service instances, such as SAP BTP services, on your Kubernetes cluster and to plain Kubernetes Secrets by adding the --on k8s
option to the cds bind
command:
cds bind -2 <service binding or secret> --on k8s
cds bind -2 <service binding or secret> --on k8s
The command uses your current Kubernetes context. That is your current server and namespace. You need to be logged in as a precondition.
Bind to Kubernetes Service Bindings
To list all Service Bindings in your current Kubernetes context, you can use the kubectl get servicebindings
command:
NAME SERVICE-INSTANCE SECRET-NAME STATUS AGE
cpapp-xsuaa-binding cpapp-xsuaa cpapp-xsuaa-secret Ready 11s
NAME SERVICE-INSTANCE SECRET-NAME STATUS AGE
cpapp-xsuaa-binding cpapp-xsuaa cpapp-xsuaa-secret Ready 11s
Use the service binding name for the -2
option:
cds bind -2 cpapp-xsuaa-binding --on k8s
cds bind -2 cpapp-xsuaa-binding --on k8s
Output:
[bind] - Retrieving data from Kubernetes...
[bind] - Binding uaa to Kubernetes service binding cpapp-xsuaa-binding with kind xsuaa
[bind] - Saving bindings to .cdsrc-private.json in profile hybrid
[bind] -
[bind] - TIP: Run with cloud bindings: cds watch --profile hybrid
[bind] - Retrieving data from Kubernetes...
[bind] - Binding uaa to Kubernetes service binding cpapp-xsuaa-binding with kind xsuaa
[bind] - Saving bindings to .cdsrc-private.json in profile hybrid
[bind] -
[bind] - TIP: Run with cloud bindings: cds watch --profile hybrid
The binding information is stored in the .cdsrc-private.json file of your project in the requires
section:
{
"requires": {
"[hybrid]": {
"auth": {
"binding": {
"type": "k8s",
"name": "cpapp-xsuaa-binding",
"cluster": "https://apiserver.d9a6204.kyma-stage.shoot.live.k8s-hana.ondemand.com",
"instance": "cpapp-xsuaa",
"namespace": "dev",
"secret": "cpapp-xsuaa-secret",
"resolved": false,
"vcap": {
"label": "xsuaa",
"plan": "application"
}
},
"kind": "xsuaa"
}
}
}
}
{
"requires": {
"[hybrid]": {
"auth": {
"binding": {
"type": "k8s",
"name": "cpapp-xsuaa-binding",
"cluster": "https://apiserver.d9a6204.kyma-stage.shoot.live.k8s-hana.ondemand.com",
"instance": "cpapp-xsuaa",
"namespace": "dev",
"secret": "cpapp-xsuaa-secret",
"resolved": false,
"vcap": {
"label": "xsuaa",
"plan": "application"
}
},
"kind": "xsuaa"
}
}
}
}
Bind to Kubernetes Secrets
Alternatively, you can bind to Kubernetes Secrets.
You can use the kubectl get secrets
command to list all secrets in your current Kubernetes context:
NAME TYPE DATA AGE
cap-hdi-container Opaque 11 44h
NAME TYPE DATA AGE
cap-hdi-container Opaque 11 44h
Use the secret name for the -2
option.
You need to provide either the service argument or the --kind
option as well, because secrets have no service metadata.
cds bind -2 cap-hdi-container --on k8s --kind hana
cds bind -2 cap-hdi-container --on k8s --kind hana
Output:
[bind] - Retrieving data from Kubernetes...
[bind] - Binding db to Kubernetes secret cap-hdi-container with kind hana
[bind] - Saving bindings to .cdsrc-private.json in profile hybrid
[bind] -
[bind] - TIP: Run with cloud bindings: cds watch --profile hybrid
[bind] - Retrieving data from Kubernetes...
[bind] - Binding db to Kubernetes secret cap-hdi-container with kind hana
[bind] - Saving bindings to .cdsrc-private.json in profile hybrid
[bind] -
[bind] - TIP: Run with cloud bindings: cds watch --profile hybrid
WARNING
If a service binding with the same name exists, cds bind
will connect to the service binding instead.
Run with Service Bindings
Run CAP Node.js Apps with Service Bindings
Now, you can run your CAP service locally using the cloud service bindings:
cds watch --profile hybrid
cds watch --profile hybrid
It will resolve the cloud bindings in your configuration:
- Bindings to Cloud Foundry: The credentials are downloaded from the service key of the Cloud Foundry API endpoint, org, and space that were targeted when
cds bind
was being called. This requires you to be logged in to the correct Cloud Foundry API endpoint. - Bindings to Kubernetes: The credentials are downloaded from the service bindings and secrets of the Kubernetes cluster and namespace that were in the current context when
cds bind
was being called.
You can also resolve and display credentials using the cds env
command:
cds env get requires.db.credentials --profile hybrid --resolve-bindings
cds env get requires.db.credentials --profile hybrid --resolve-bindings
Example output:
{
url: 'jdbc:sap://BDB9AC0F20CB46B494E6742047C4F99A.hana.eu10.hanacloud.ondemand.com:443?encrypt=true&validateCertificate=true¤tschema=BDB9AC0F20CB46B494E6742047C4F99A',
host: 'bdb9ac0f20cb46b494e6742047c4f99a.hana.eu10.hanacloud.ondemand.com',
port: '443',
driver: 'com.sap.db.jdbc.Driver',
schema: 'BDB9AC0F20CB46B494E6742047C4F99A',
hdi_user: 'BDB9AC0F20CB46B494E6742047C4F99A_DT',
hdi_password: 'abc...xyz',
user: 'BDB9AC0F20CB46B494E6742047C4F99A_RT',
password: 'abc....xyz',
certificate: '-----BEGIN CERTIFICATE-----\n' +
'...' +
'-----END CERTIFICATE-----'
}
{
url: 'jdbc:sap://BDB9AC0F20CB46B494E6742047C4F99A.hana.eu10.hanacloud.ondemand.com:443?encrypt=true&validateCertificate=true¤tschema=BDB9AC0F20CB46B494E6742047C4F99A',
host: 'bdb9ac0f20cb46b494e6742047c4f99a.hana.eu10.hanacloud.ondemand.com',
port: '443',
driver: 'com.sap.db.jdbc.Driver',
schema: 'BDB9AC0F20CB46B494E6742047C4F99A',
hdi_user: 'BDB9AC0F20CB46B494E6742047C4F99A_DT',
hdi_password: 'abc...xyz',
user: 'BDB9AC0F20CB46B494E6742047C4F99A_RT',
password: 'abc....xyz',
certificate: '-----BEGIN CERTIFICATE-----\n' +
'...' +
'-----END CERTIFICATE-----'
}
WARNING
Only cds watch
and cds env
(the latter with the --resolve-bindings
option) resolve cloud bindings. Bindings are resolved by cds serve
or cds exec
.
Run Arbitrary Commands with Service Bindings
With cds bind
you avoid storing credentials on your hard disk. If you need to start other applications with cloud service bindings from local, then you can use the exec
sub command of cds bind
.
For example, you can run the approuter from the approuter
child directory:
cds bind --exec -- npm start --prefix approuter
cds bind --exec -- npm start --prefix approuter
cds bind --exec -- npm start --prefix approuter
cds bind --exec -- npm start --prefix approuter
cds bind --exec '--' npm start --prefix approuter
cds bind --exec '--' npm start --prefix approuter
This works by building up a VCAP_SERVICES
variable from the bindings in the chosen profiles (default: hybrid
). You can run the following command to print the content of the generated VCAP_SERVICES
variable:
cds bind --exec -- node -e 'console.log(process.env.VCAP_SERVICES)'
cds bind --exec -- node -e 'console.log(process.env.VCAP_SERVICES)'
cds bind --exec -- node -e 'console.log(process.env.VCAP_SERVICES)'
cds bind --exec -- node -e 'console.log(process.env.VCAP_SERVICES)'
cds bind --exec '--' node -e 'console.log(process.env.VCAP_SERVICES)'
cds bind --exec '--' node -e 'console.log(process.env.VCAP_SERVICES)'
Run CAP Java Apps with Service Bindings
Start your CAP Java application with the cds bind --exec
command to use the service bindings.
For example:
cds bind --exec mvn spring-boot:run
cds bind --exec mvn spring-boot:run
Run with Service Bindings from a Running Cloud Application
Run CAP Apps Using Cloud Application Bindings
Instead of binding to specific cloud services, you can run your application with all service bindings of an application on the SAP BTP, Cloud Foundry environment.
TIP
But you need to have (1) your application deployed, and (2) be logged in to your Cloud Foundry space using the cf
command line.
For example, you can use the following syntax with bash
or similar shells:
VCAP_SERVICES=$(cf env <CF-APP-NAME> | perl -0pe '/VCAP_SERVICES:(.*?)VCAP_APPLICATION:/smg; $_=$1') cds watch --profile hybrid
VCAP_SERVICES=$(cf env <CF-APP-NAME> | perl -0pe '/VCAP_SERVICES:(.*?)VCAP_APPLICATION:/smg; $_=$1') cds watch --profile hybrid
Your profile should have the kind
settings to use the bound services, for example requires.db = hana
.
cds bind
Usage
By Cloud Service Only
The shortest way to use cds bind
is to specify only the Cloud Foundry service instance name:
cds bind -2 my-hana
cds bind -2 my-hana
This implies that a service key exists with the suffix -key
. In this example: my-hana-key
.
You can specify a different key after a colon (":
"):
cds bind -2 my-hana:my-different-key
cds bind -2 my-hana:my-different-key
With CDS Service and Kind
If kind
or CDS service
cannot be determined automatically by cds bind
, you need to specify it:
cds bind credstore -2 my-credstore --kind credstore
cds bind credstore -2 my-credstore --kind credstore
You are informed with an error message if this is required.
Bind Multiple Services with One Command
There is a handy shortcut to bind multiple services with one command:
cds bind -2 my-hana,my-destination,my-xsuaa
cds bind -2 my-hana,my-destination,my-xsuaa
TIP
This shortcut is only possible if you don't need to provide a service
or a kind
.
With Profile and Output File
By default, the bindings for the hybrid
profile are stored in the .cdsrc-private.json file in your current working directory.
This can be overwritten using the --profile
and --output-file
options.
Execute Commands with Bindings
You can start arbitrary command line programs with your bindings.
The service bindings are resolved from the cloud and provided in the VCAP_SERVICES
env variable to the application. So it works with every application that can consume Cloud Foundry credentials.
cds bind --exec [--] <command> <args ...>
cds bind --exec [--] <command> <args ...>
Use the double-dash (--
) if your command has args starting with a dash (-
) character. Otherwise the cds
command line will try to parse them as their own options.
On PowerShell you need to quote the double dash (--
) when an option with double dash follows, e.g.
cds bind --exec '--' somecmd --someflag --some-double-dash-parameter 42
cds bind --exec '--' somecmd --someflag --some-double-dash-parameter 42
Profiles can be set using the optional --profile
parameter. By default the hybrid
profile is used.
cds bind --exec --profile <profile> [--] <command> <args ...>
cds bind --exec --profile <profile> [--] <command> <args ...>
The --profile
parameter must follow exec
directly.
Use Cases
Most of the following use cases are shown for Node.js, but can be easily adapted for Java.
Destinations
Learn how to connect to remote services from local using SAP BTP destinations.
Authentication and Authorization using XSUAA
Learn how to do hybrid testing using the XSUAA service in the CAP Node.js authentication documentation.
Integration Tests
cds bind
can be handy for testing with real cloud services in your CI/CD pipeline.
Configure your required bindings for testing and save them to your project's package.json file for your tests' profile:
cds bind -2 integration-test-hana -o package.json -p integration-test
cds bind -2 integration-test-hana -o package.json -p integration-test
No credentials are saved!
In your CI/CD pipeline you can resolve the bindings and inject them into the test commands:
# Install DK for "cds env"
npm i @sap/cds-dk --no-save
# Login
cf auth $USER $PASSWORD
## Uncomment if your service bindings have
## no "org" and "space" set (see note below)
# cf target -o $ORG -s $SPACE
# Set profile
export CDS_ENV=integration-test
# Set resolved bindings
export cds_requires="$(cds env get requires --resolve-bindings)"
# Execute test
npm run integration-test
# Install DK for "cds env"
npm i @sap/cds-dk --no-save
# Login
cf auth $USER $PASSWORD
## Uncomment if your service bindings have
## no "org" and "space" set (see note below)
# cf target -o $ORG -s $SPACE
# Set profile
export CDS_ENV=integration-test
# Set resolved bindings
export cds_requires="$(cds env get requires --resolve-bindings)"
# Execute test
npm run integration-test
With CDS_ENV
, you specify the configuration profile for the test, where you previously put the service binding configuration.
cds env get requires
prints the requires
section of the configuration as a JSON string. By adding the --resolve-bindings
option, it includes the credentials of the service bindings from the cloud. To make the credentials available for all subsequent cds
commands and the tests, the requires
JSON string is put into the cds_requires
variable.
TIP
Service bindings created by cds bind
contain the Cloud Foundry API endpoint, org, and space. You can allow your services to connect to the currently targeted Cloud Foundry org and space by removing these properties from the binding structure.