Files
2025-11-30 08:55:02 +08:00

401 lines
8.4 KiB
Markdown

# SAP BTP Testing Reference
## Overview
Quality assurance requires comprehensive testing across multiple dimensions including UI, usability, performance, and unit testing.
## Testing Strategy
### Test Pyramid
```
/\
/ \ E2E/UI Tests (few, slow)
/----\
/ \ Integration Tests (some)
/--------\
/ \ Unit Tests (many, fast)
/------------\
```
### Benefits of Unit Testing
- Detect issues fast
- Better maintainable code
- More understandable code
- CI/CD integration
- Regression prevention
## CAP Testing
### Jest Setup
```javascript
// package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"devDependencies": {
"jest": "^29.0.0",
"@sap/cds-dk": "^7.0.0"
}
}
```
### Unit Test Example
```javascript
// test/unit/catalog-service.test.js
const cds = require('@sap/cds');
describe('CatalogService', () => {
let srv;
beforeAll(async () => {
srv = await cds.connect.to('CatalogService');
});
afterAll(async () => {
await cds.disconnect();
});
describe('READ Books', () => {
it('should return all books', async () => {
const books = await srv.read('Books');
expect(books).toBeDefined();
expect(Array.isArray(books)).toBe(true);
});
it('should filter by title', async () => {
const books = await srv.read('Books').where({ title: 'Test Book' });
expect(books.length).toBeLessThanOrEqual(1);
});
});
describe('CREATE Books', () => {
it('should create a book', async () => {
const book = await srv.create('Books', {
title: 'New Book',
stock: 10
});
expect(book.ID).toBeDefined();
expect(book.title).toBe('New Book');
});
it('should reject invalid data', async () => {
await expect(srv.create('Books', {}))
.rejects.toThrow();
});
});
});
```
### Integration Test Example
```javascript
// test/integration/api.test.js
const cds = require('@sap/cds');
const { GET, POST, PATCH, DELETE } = cds.test(__dirname + '/..');
describe('API Integration', () => {
it('GET /catalog/Books', async () => {
const { status, data } = await GET('/catalog/Books');
expect(status).toBe(200);
expect(data.value).toBeDefined();
});
it('POST /catalog/Books', async () => {
const { status, data } = await POST('/catalog/Books', {
title: 'Test Book',
stock: 5
});
expect(status).toBe(201);
expect(data.ID).toBeDefined();
});
it('GET /catalog/Books/:id', async () => {
const { data: created } = await POST('/catalog/Books', { title: 'Test' });
const { status, data } = await GET(`/catalog/Books(${created.ID})`);
expect(status).toBe(200);
expect(data.title).toBe('Test');
});
});
```
## SAPUI5 Testing
### QUnit Framework
**Best Practice**: Write small, focused tests
```javascript
// webapp/test/unit/model/formatter.js
sap.ui.define([
"my/app/model/formatter"
], function(formatter) {
"use strict";
QUnit.module("formatter");
QUnit.test("formatStatus", function(assert) {
assert.strictEqual(
formatter.formatStatus("OPEN"),
"Open",
"Status formatted correctly"
);
});
QUnit.test("formatDate", function(assert) {
const date = new Date("2024-01-15");
assert.strictEqual(
formatter.formatDate(date),
"Jan 15, 2024",
"Date formatted correctly"
);
});
});
```
### OPA5 Integration Tests
```javascript
// webapp/test/integration/pages/List.js
sap.ui.define([
"sap/ui/test/Opa5",
"sap/ui/test/actions/Press"
], function(Opa5, Press) {
"use strict";
Opa5.createPageObjects({
onTheListPage: {
actions: {
iPressOnFirstItem: function() {
return this.waitFor({
controlType: "sap.m.ObjectListItem",
success: function(aItems) {
new Press().executeOn(aItems[0]);
}
});
}
},
assertions: {
iShouldSeeTheList: function() {
return this.waitFor({
id: "list",
success: function() {
Opa5.assert.ok(true, "List is visible");
}
});
}
}
}
});
});
```
### OPA5 Journey
```javascript
// webapp/test/integration/AllJourneys.js
sap.ui.define([
"sap/ui/test/Opa5",
"./pages/List",
"./ListJourney"
], function(Opa5) {
"use strict";
Opa5.extendConfig({
viewNamespace: "my.app.view.",
autoWait: true
});
});
// webapp/test/integration/ListJourney.js
sap.ui.define([
"sap/ui/test/opaQunit"
], function(opaTest) {
"use strict";
QUnit.module("List Journey");
opaTest("Should see the list", function(Given, When, Then) {
Given.iStartMyApp();
Then.onTheListPage.iShouldSeeTheList();
Given.iTeardownMyApp();
});
opaTest("Should navigate to detail", function(Given, When, Then) {
Given.iStartMyApp();
When.onTheListPage.iPressOnFirstItem();
Then.onTheDetailPage.iShouldSeeTheDetail();
Given.iTeardownMyApp();
});
});
```
## ABAP Testing
### ABAP Unit
```abap
CLASS ltcl_travel DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA: mo_environment TYPE REF TO if_cds_test_environment.
DATA: mo_cut TYPE REF TO zcl_travel_handler.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS setup.
METHODS test_validate_dates FOR TESTING.
METHODS test_calculate_total FOR TESTING.
ENDCLASS.
CLASS ltcl_travel IMPLEMENTATION.
METHOD class_setup.
mo_environment = cl_cds_test_environment=>create(
i_for_entity = 'ZI_TRAVEL'
).
ENDMETHOD.
METHOD class_teardown.
mo_environment->destroy( ).
ENDMETHOD.
METHOD setup.
mo_environment->clear_doubles( ).
mo_cut = NEW #( ).
ENDMETHOD.
METHOD test_validate_dates.
" Given
DATA(lv_begin) = cl_abap_context_info=>get_system_date( ).
DATA(lv_end) = lv_begin + 7.
" When
DATA(lv_valid) = mo_cut->validate_dates(
iv_begin = lv_begin
iv_end = lv_end
).
" Then
cl_abap_unit_assert=>assert_true( lv_valid ).
ENDMETHOD.
METHOD test_calculate_total.
" Given
DATA(lv_booking_fee) = CONV decfloat16( '50.00' ).
DATA(lv_price) = CONV decfloat16( '500.00' ).
" When
DATA(lv_total) = mo_cut->calculate_total(
iv_booking_fee = lv_booking_fee
iv_price = lv_price
).
" Then
cl_abap_unit_assert=>assert_equals(
exp = '550.00'
act = lv_total
).
ENDMETHOD.
ENDCLASS.
```
### CDS Test Double Framework
```abap
" Create test doubles for CDS entities
DATA(lo_env) = cl_cds_test_environment=>create(
i_for_entity = 'ZI_TRAVEL'
).
" Insert test data
DATA: lt_travel TYPE TABLE OF ztravel.
lt_travel = VALUE #(
( travel_uuid = cl_uuid_factory=>create_uuid_c32( )
travel_id = '1'
customer_id = 'CUST001'
begin_date = '20240101'
end_date = '20240108' )
).
lo_env->insert_test_data( lt_travel ).
" Run tests...
" Clear after tests
lo_env->clear_doubles( ).
```
## Test Automation
### CI/CD Integration
```yaml
# .pipeline/config.yml
stages:
Additional Unit Tests:
npmExecuteScripts: true
npmScripts:
- test
Integration Tests:
npmExecuteScripts: true
npmScripts:
- test:integration
UI Tests:
karmaExecuteTests: true
```
### Code Coverage
```javascript
// jest.config.js
module.exports = {
collectCoverage: true,
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};
```
## Best Practices
### Unit Tests
1. Test one thing at a time
2. Use descriptive names
3. Follow AAA pattern (Arrange, Act, Assert)
4. Mock external dependencies
5. Keep tests fast
### Integration Tests
1. Test real interactions
2. Use test data that represents production
3. Clean up after tests
4. Test error paths
### UI Tests
1. Test user journeys
2. Use page objects pattern
3. Keep tests maintainable
4. Test accessibility
## Source Documentation
- Testing: [https://github.com/SAP-docs/btp-developer-guide/blob/main/docs/performing-ui-usability-and-unit-tests-50a7c7d.md](https://github.com/SAP-docs/btp-developer-guide/blob/main/docs/performing-ui-usability-and-unit-tests-50a7c7d.md)
- SAPUI5 Testing: [https://sapui5.hana.ondemand.com/#/topic/291c9121e6044ab381e0b51716f97f52](https://sapui5.hana.ondemand.com/#/topic/291c9121e6044ab381e0b51716f97f52)
- CAP Testing: [https://cap.cloud.sap/docs/node.js/cds-test](https://cap.cloud.sap/docs/node.js/cds-test)