Initial commit
This commit is contained in:
400
references/testing.md
Normal file
400
references/testing.md
Normal file
@@ -0,0 +1,400 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user