首页 / 资讯中心 / 文章详情

SAP ABAP调用RESTful API的测试用例框架实战

SAP ABAP调用RESTful API的测试用例框架实战 ★ FEATURED ARTICLE
1. 项目背景与核心价值做SAP开发的朋友迟早都会撞上这么一堵墙——周边系统越来越多订单、物流、支付、主数据各种业务系统都在往外吐接口。你这边还在用RFC、IDoc跟老伙计们打交道人家隔壁团队新上的平台却只认RESTful API。怎么办硬着头皮学呗。这个项目的核心就是在SAP ABAP环境里写一套能稳定调用RESTful API的测试用例。说白了就是用ABAP这门老语言去敲新一代接口的门。我把它做成了一个轻量级的可复用框架不依赖外部工具直接在SE38里跑既能测通GET/POST/PUT/DELETE这些基础动作也能处理鉴权、超时、报文解析这些绕不开的细节。这个内容适合谁看两类人。一类是刚接触接口开发的ABAP工程师想找个能直接抄的作业另一类是做系统集成的顾问需要验证SAP跟外部平台的连通性但又不想每次都拉上Java或者Python同事帮忙。我自己就是从这个坑里爬出来的所以把这套东西整理成文希望能帮你少走点弯路。项目核心解决的痛点很明确一是ABAP原生语法对RESTful支持得比较别扭二是调试HTTP接口时报文乱、报错玄学三是常规文档只讲成功路径不讲失败怎么查。所以这套测试用例设计的重点不在于把代码写得多花哨而在于把调用链路的每一步都拆开、看清楚出了问题能精准定位是网络、鉴权、报文还是对端系统的问题。2. 整体设计与思路拆解2.1 为什么不用类CL_HTTP_CLIENT很多ABAP老手第一反应是SAP不是自带IF_HTTP_CLIENT嘛直接用它调不就完了。没错IF_HTTP_CLIENT确实能用但它有几个很实际的问题。首先是版本差异大。在老一点的ECC版本上IF_HTTP_CLIENT的参数传递方式、响应读取方式跟S/4 HANA上有不少差别你一段代码抄过来可能编译都过不了。其次是对RESTful语义的支持很弱比如HTTP状态码判断、Header操作、JSON解析都得自己手工拼。最要命的是老代码里充斥着被标记为过时的METHOD每次升级都要跟着改维护成本很高。所以我选择了另一条路直接用IF_HTTP_CLIENT的增强玩法——结合系统标准类CL_HTTP_CLIENT来构建请求同时自己封装一层Json解析器。这套方案的好处是底层调用还是SAP标准能力稳定性有保障上层封装完全由自己控制接口统一测试用例写起来顺手参数调整灵活。2.2 测试用例框架的分层设计这套测试用例没有走复杂的BDD或者TDD路线而是采用了简单粗暴但非常实用的三层设计第一层基础工具类负责HTTP请求的发送、响应接收、状态码提取第二层业务接口封装每个外部接口对应一个方法把URL、Header、Body的组装细节藏起来第三层测试用例主体按业务场景组织一个场景一个REPORT或一个FORM。这样分层的好处是底层改动不影响业务层比如超时时间从5秒改成15秒只需要动工具类新增接口也不会污染已有用例复制一个方法改改参数就行。实测下来维护成本比把所有逻辑堆在一个REPORT里低了不是一星半点。2.3 为什么强调测试用例而不仅是调用代码项目标题里特意写了测试用例三个字这是有讲究的。因为我们在SAP里调外部接口最大的痛苦不是你调不通而是你调通了但不知道它什么时候会断、断在哪里。普通调用代码只能告诉你成功或失败而一套好的测试用例能告诉你失败在哪一步、为什么失败、对端返回了什么。我的做法是每个测试用例不仅输出最终结果还会把请求报文、响应报文、耗时、HTTP状态码、错误码全部记录到内存表里最后统一打印或者写入日志表。这样即便接口出问题你也能拿着完整链路去跟对端系统对质不用猜。3. 核心细节解析与实操要点3.1 HTTP客户端创建的三种方式与选择在ABAP里创建一个HTTP客户端常规有三种做法我分别踩过坑说下结论。第一种是直接NEW一个CL_HTTP_CLIENT实例。这种方式最简单但它不会自动带上SAP的代理配置也不绑定目的地适合纯内网直连的场景。如果你要测试的环境不需要走代理用它最省事。第二种是通过CL_HTTP_CLIENT_CREATE_BY_DESTINATION创建绑定一个SM59配置的逻辑目的地。这种方式最推荐因为代理、超时、SSL配置都可以在SM59里统一管理测试用例代码里不用写死任何网络参数。切换环境的时候只改SM59指向就行。第三种是CL_HTTP_CLIENT_CREATE_BY_CONFIGURATION基于配置文件创建适合有统一HTTP配置管理的团队。但这个在多数项目里用得不多因为大部分ABAP环境还没有引入这套配置体系。我实际选的是第二种。原因很朴素项目上要连的外部环境不止一个测试环境、预发环境、生产环境各有一套地址用SM59管理代码零改动只调配置干净利落。3.2 Header与Content-Type的细节处理RESTful API对Header的要求往往比SOAP严格得多。最常见的一个坑是Content-Type没设对对端直接给你返回415或者400。我见过很多新手在这上面卡半天。一般GET请求需要设置lv_header_name Content-Type. lv_header_value application/json. o_client-request-set_header_field( EXPORTING name lv_header_name value lv_header_value ).但POST、PUT请求光设Content-Type还不够很多对端还要校验Accept头。我的建议是统一把这两个都设置上避免对端返回内容格式跟预期不符。另外如果你的接口需要传Token常见的位置有两种一种是放在Authorization头里比如Bearer Token一种是放在自定义头里比如X-Access-Token。这两种在代码里都是同样的set_header_field操作但Token从哪来就得看你项目的鉴权方式了。后面我会单独讲鉴权这块。3.3 响应处理的三大关键点收到响应之后不要急着解析Body先做三件事。第一步是看状态码。2xx是成功4xx基本是请求报文有问题5xx是对端服务异常。但很多接口返回HTTP 200业务上却是失败的比如返回码是5001之类的错误码。所以单看HTTP状态码是远远不够的还要解析Body里的业务状态。第二步是取响应Body。注意响应可能是压缩的如果对端返回了Content-Encoding: gzip而你的HTTP客户端没做解压处理拿到的就是乱码。这时候要么让对端关闭压缩要么在请求头里明确声明不接受压缩格式。我项目里有个接口就是用gzip返回的折腾了半天才发现是这个原因。第三步是记录响应耗时。在发送请求前记一下时间戳收到响应后算差值这个数据非常有用。比如某个接口平时50毫秒突然变成3秒那基本可以断定是对端出问题了。4. 实操过程与核心环节实现4.1 环境准备与基础工具类实现我的开发环境是S/4 HANA 1709版本ABAP版本为7.51。这套代码理论上在ECC 6.0 EHP7以上都能跑但老版本的话需要手动确认类是否存在。先建一个工具类取名ZCL_REST_CLIENT用来封装HTTP调用逻辑。核心方法就是一个叫SEND_REQUEST入参是URL、Method、Header表、Body字符串出参是响应Body和状态码。CLASS zcl_rest_client DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. TYPES: BEGIN OF ty_header, name TYPE string, value TYPE string, END OF ty_header, ty_header_tab TYPE TABLE OF ty_header. METHODS: send_request IMPORTING iv_url TYPE string iv_method TYPE string it_header TYPE ty_header_tab OPTIONAL iv_body TYPE string OPTIONAL EXPORTING ev_response TYPE string ev_status_code TYPE i ev_status_text TYPE string ev_error_text TYPE string RAISING cx_root. ENDCLASS.实际实现里发送请求的核心代码是METHOD send_request. DATA: lo_client TYPE REF TO if_http_client, lv_url TYPE string, lv_method TYPE string, lv_body TYPE string, ls_header LIKE LINE OF it_header. lv_url iv_url. lv_method iv_method. lv_body iv_body. 这里使用SM59里配置的目的地我这里取名ZEXTERNAL_API cl_http_clientcreate_by_destination( EXPORTING destination ZEXTERNAL_API IMPORTING client lo_client EXCEPTIONS argument_not_found 1 destination_not_found 2 internal_error 3 OTHERS 4 ). IF sy-subrc 0. ev_error_text HTTP客户端创建失败请检查SM59配置. RETURN. ENDIF. 设置请求方法 lo_client-request-set_method( lv_method ). 设置请求URL lo_client-request-set_header_field( EXPORTING name ~request_uri value lv_url ). 设置请求Header LOOP AT it_header INTO ls_header. lo_client-request-set_header_field( EXPORTING name ls_header-name value ls_header-value ). ENDLOOP. 如果请求体不为空设置请求体内容 IF lv_body IS NOT INITIAL. lo_client-request-set_cdata( lv_body ). ENDIF. 发送请求 lo_client-send( EXCEPTIONS http_communication_failure 1 http_invalid_state 2 http_processing_failed 3 OTHERS 4 ). IF sy-subrc 0. 通信异常时从客户端里取错误信息 ev_error_text lo_client-get_last_error( ). RETURN. ENDIF. 接收响应 lo_client-receive( EXCEPTIONS http_communication_failure 1 http_invalid_state 2 http_processing_failed 3 OTHERS 4 ). IF sy-subrc 0. ev_error_text lo_client-get_last_error( ). RETURN. ENDIF. 提取状态码 ev_status_code lo_client-response-get_status( ). ev_status_text lo_client-response-get_status_text( ). 提取响应内容 ev_response lo_client-response-get_cdata( ). 关闭连接 lo_client-close( ). ENDMETHOD.这段代码看起来简单但有几个细节需要特别注意。第一~request_uri这个Header名不要拼错很多教程里根本没提这个导致URL设置无效对端一直收到空路径。第二创建HTTP客户端时的EXCEPTIONS一定要写全否则异常抛出来你的程序直接崩溃连排查的机会都没有。第三每次调用完记得CLOSE否则连接池会被占满我生产上遇到过SAP应用服务器HTTP连接数被打满的情况就是这个原因。4.2 GET请求测试用例完整演示有了工具类写一个GET请求的测试就非常顺手了。我拿一个模拟的外部工单查询接口来演示。REPORT ztest_rest_get. DATA: lo_client TYPE REF TO zcl_rest_client, lt_header TYPE zcl_rest_clientty_header_tab, ls_header LIKE LINE OF lt_header, lv_resp TYPE string, lv_status TYPE i, lv_stext TYPE string, lv_err TYPE string. START-OF-SELECTION. CREATE OBJECT lo_client. 设置Header头 ls_header-name Content-Type. ls_header-value application/json. APPEND ls_header TO lt_header. ls_header-name Accept. ls_header-value application/json. APPEND ls_header TO lt_header. 如果有Token取消下面注释并按实际调整 ls_header-name Authorization. ls_header-value Bearer xxxxxxx. APPEND ls_header TO lt_header. 调用GET接口 lo_client-send_request( EXPORTING iv_url /api/workorder/query iv_method GET it_header lt_header IMPORTING ev_response lv_resp ev_status_code lv_status ev_status_text lv_stext ev_error_text lv_err ). 输出结果 WRITE: / HTTP状态码:, lv_status. WRITE: / 状态文本:, lv_stext. IF lv_err IS NOT INITIAL. WRITE: / 通信错误:, lv_err. ELSE. WRITE: / 响应报文:, lv_resp. ENDIF.这里有一个很重要的设计URL我写的是相对路径不带IP和端口。因为IP和端口已经配置在SM59的目的地里了代码里写相对路径有两个好处。一是测试用例代码可以在任何环境直接跑不用改地址二是代码里不会暴露环境信息项目上做代码传输的时候就少了一道脱敏的麻烦。4.3 POST请求与请求体构造POST请求比GET多一个动作就是设置请求体。但请求体的构造恰恰是重灾区。外部系统的接口定义五花八门有的要求根节点是对象有的是数组有的字段命名是下划线风格有的是驼峰。我的建议是在测试用例里先用字符串拼接出一个最小可用请求体跑通了之后再考虑用JSON转换工具动态构造。下面是一段实际可用的POST请求体构造代码DATA: lv_body TYPE string. CONCATENATE { orderNo:SO-20240815-001, customerCode:C001, itemList:[ { materialCode:MAT001, quantity:2, unitPrice:99.5 }, { materialCode:MAT002, quantity:1, unitPrice:59 } ] } INTO lv_body.用CONCATENATE拼字符串是笨办法但胜在直观。新手调试阶段用这种方式可以准确知道发送出去的报文长什么样。等接口稳定了再用/data/cl_json或者/UI2/CL_JSON把内部表转JSON能写得更优雅但出错时反而不容易定位。POST请求发送代码和GET基本一样只是Method换成POST然后把lv_body传给IV_BODY参数。但这里有一个容易踩的坑如果接口返回201 Created而你的工具类只是判断状态码是否等于200就会误判为失败。所以工具类里对状态码的判断建议放宽到2xx才算成功不要死等200。4.4 鉴权流程的四种常见处理方式外部接口的认证五花八门我梳理了一下ABAP调用RESTful API常见的鉴权方式基本是四种你们对号入座。第一种是Basic Auth最简单用户名密码冒号连接后做Base64编码放进Authorization头。这种方式在ABAP里用字符串拼接加编码就能搞定不需要额外类库。第二种是Token模式先调一个登录接口获取Token然后把它缓存起来后续请求都带着它。ABAP里可以用类CL_MEMORY_INTERN管理这个Token避免每次请求都重新登录。第三种是OAuth 2.0的Client Credentials模式需要先拿Client ID和Client Secret去换Access Token。这个稍微复杂但原理跟第二种一样只是换Token的接口和参数格式有固定套路。我用一个类CL_OAUTH_CLIENT来封装了一下实测可用。第四种是SSL双向认证这种需要导入证书到SAP的证书库然后在SM59目的地里配置SSL证书。ABAP代码里不需要做什么特殊处理但证书过期是家常便饭项目上务必做好监控。不管哪种鉴权方式我都有一个建议测试用例里不要把真实密码或者Token硬编码进去。要么从配置表里读要么通过PARAMETER传入。否则代码一传输等于把凭据分发到全公司了这是很要命的低级错误。5. 常见问题与排查技巧实录5.1 HTTP 400 Bad Request的排查方法HTTP 400是ABAP调RESTful接口时最常遇到的问题没有之一。对端返回400说明你的请求报文有语法或语义问题但对端又不告诉你具体是哪错了。我的排查套路是三步走。第一步把发送的原始报文完整打出来用工具类的日志记录别靠猜。第二步去试同一个接口用现成的接口测试工具比如某个开源的API调试工具发同样的报文如果工具能通而ABAP不通那就是报文在ABAP里被改动过重点检查字符串拼接时的引号和中文转义。第三步如果工具也报400那就是报文内容本身不对跟ABAP无关去跟对端对字段定义。我碰到过一个实际案例某个字段在接口文档里是createTime我按驼峰写的结果对端要求是create_timeABAP里字符串硬拼的话根本不报错但对端直接400。这种问题查起来最费时间因为错误信息基本没有参考价值。5.2 超时与连接池耗尽问题ABAP调用外部HTTP接口默认的超时时间是60秒。这个时间在处理大量数据同步的场景下可能不够尤其对端是大数据平台你发个查询请求它要跑几分钟才有结果。超时的设置方式是在发送请求前调用lo_client-propertytype_accept_cookie if_http_clientco_enabled. lo_client-propertytype_logon_user if_http_clientco_unknown.严格来讲超时一般是设置LO_CLIENT-PROPERTYTYPE_TIMEOUT比如设置为300秒lo_client-propertytype_timeout 300.但更常用的做法是在SM59目的地的高级设置里把超时时间调大。如果某个接口很特殊只在极少数场景下才需要长超时可以在业务封装层单独创建一个专用目的地不影响其他接口的默认配置。连接池耗尽是另外一个容易被忽略的问题。SAP应用服务器到外部系统的HTTP连接数是有限制的如果每次调用不CLOSE连接就会挂在那儿直到超时被回收期间新的请求只能排队等。我遇到过最夸张的一次Java服务那边正常SAP这边所有HTTP调用全部超时查到最后就是某个巡检程序忘了加CLOSE连接池被占满了。5.3 响应中文乱码的根治方案ABAP里的Encoding问题非常阴间。多数外部系统返回的JSON都是UTF-8编码但ABAP内部字符串默认可能是非Unicode环境的系统编码。如果你直接get_cdata读出来的中文就是一堆问号或者乱码。我的解决方案是在接收响应后、解析Body前做一次显式编码转换。具体做法是先把响应内容当成XSTRING读出来然后按UTF-8转换到字符串。DATA: lv_xstring TYPE xstring. DATA: lv_utf8 TYPE string. lv_xstring lo_client-response-get_cdata( ). 这里使用系统类转换编码 CALL METHOD cl_http_utilitydecode_xstring_to_utf8 EXPORTING encoded lv_xstring RECEIVING decoded lv_utf8.这个处理在工具类中做一次所有测试用例都能受益。另外要注意的是发送请求体的时候如果Body里有中文也要确认对端能接受UTF-8编码否则就要在Header里设置Content-Type: text/json; charsetutf-8之类的内容。5.4 JSON解析的常见坑与插件选择ABAP原生没有JSON解析器但系统提供了几个标准工具类。老一点的项目里用得比较多的是CL_TREX_JSON_SERIALIZER新版本里有更顺手的CL_JSON接口。这里有个很实用的经验CL_TREX_JSON_SERIALIZER在处理JSON数组转内部表的时候默认行为和很多人的预期不一样经常出现结构对不上、数据丢失的问题。如果你用这个类解析总是报转换错误别死磕直接换CL_JSON试试大概率能解决。但不管用哪个类JSON解析最大的坑是字段类型不匹配。比如JSON里某个字段是数字ABAP内表里对应字段定义成CHAR解析的时候就会直接报错。我建议在解析之前先通过simple transformation或者XSLT做一个类型检查或者干脆把内表字段全部定义为STRING解析成功后再做类型转换。后一种方式虽然绕但胜在稳定适合接口字段频繁变动的项目。6. SM59配置要点与多环境切换6.1 SM59目的地创建的完整步骤这套测试用例能不能跑起来一半靠代码一半靠SM59配置。配置不对代码写得再漂亮也白搭。进入事务码SM59创建新的HTTP目的地主要填几个地方目标主机地址填外部系统的IP或域名服务端口号比如8080、8443协议选HTTP还是HTTPS路径前缀如果外部接口统一都有个前缀填这里代码里只写相对路径代理设置如果环境需要代理这里配置否则留空。创建完成后用SM59自带的连接测试功能先测一把。这一步能排除很多低级问题比如IP不通、端口被防火墙挡了、证书不匹配等。千万别一上来就调代码大概率是浪费时间。6.2 测试环境与生产环境的无缝切换项目上最烦的就是环境切换。测试环境的URL是test.api.example.com生产是api.example.com如果代码里写死地址每次上线都要改代码、走传输麻烦不说还容易出现生产事故。我的做法是同一套代码配置两个SM59目的地一个叫ZEXTERNAL_API_TEST一个叫ZEXTERNAL_API_PROD。测试用例默认连测试目的地要验证生产时临时改一下工具类里的目的地名称常量加上一个写死的切换标记。或者在测试用例程序里增加一个参数允许运行时选择目的地名称。更彻底的做法是目的地名不写死在代码里而是放到一个配置表里用部署环境做关键字去读。但大多数项目没那么复杂做到“改一个变量就算切换”已经完全够用了。6.3 证书与SSL的注意事项如果是HTTPS接口SM59里要指定SSL证书。SAP的证书管理在STRUST事务码里需要把对端的CA证书导入进去。这个步骤容易出问题的地方是证书链不完整或者证书要求是PEM格式但你导入了DER格式。实测下来最简单的自测方法还是SM59里的连接测试。如果报SSL握手失败八成是证书问题。这时候把对端提供的完整证书链包含根证书和中间证书全部导入基本能解决。另外提醒一下很多测试环境的证书是自签的SAP默认会拒绝。这时候需要在STRUST里把测试环境的证书设置为可信。我见过不止一个项目代码写得好好的结果在测试环境因为证书问题无法验证白白浪费了好几天。7. 使用体会与扩展建议7.1 这套方案在真实项目中的表现这套测试用例框架我在一个电商集成的模拟项目中用了差不多三个月。期间接了四五个外部接口包括订单查询、库存预留、物流状态回传、支付结果通知有GET有POST有同步有异步回调。整体跑下来代码本身的改动很少更多的是调SM59配置和排查对端接口的参数变化。最让我满意的一点是业务方排障的时间大幅缩短。以前接口出问题第一反应是找ABAP开发看代码现在直接把测试用例跑一遍请求报文、响应报文、状态码全部打出来是哪端的问题一目了然。有两次是外部系统那边改了字段名导致我们发送的报文校验失败测试用例的完整日志直接把问题暴露了省去了好几轮“你查查那边”“那边没问题”的扯皮。7.2 后续扩展的三个方向如果这套基础框架已经跑通了我建议再往三个方向扩展。第一个方向是加自动化回归能力。用ABAP的定时任务调度测试用例每天凌晨跑一遍核心接口联通性测试输出日报提前发现接口漂移或者证书过期的问题。第二个方向是加响应时间监控。现在的测试用例只记录单次耗时如果能加个历史存储表按接口维度汇总平均耗时、最大耗时就能在外部系统性能劣化时第一时间发现。第三个方向是加JSON Schema校验。对端接口改字段时如果测试用例能自动检查响应报文是否还符合约定的结构就能避免“接口通但业务数据解析不出来”的隐性故障。7.3 最后分享一个小技巧调RESTful接口的时候很多人忽略了一个细节SAP的HTTP客户端默认可能会自动追加一些Header或者对URL做一些处理。如果你发现对端收到的报文和代码里设置的不一致可以用Wireshark或者直接在对端系统上加日志来对比。另外如果外部系统要求精确控制HTTP协议版本比如强制用HTTP/1.1而不是HTTP/1.0记得在SM59目的地的高级选项卡里设置协议版本。有些老的接口实现在HTTP/1.0下行为会变响应返回不完整但ABAP端看起来只是数据少了一段非常误导排查方向。我自己在这上面吃过一次亏整整调了一个下午最后发现是协议版本的问题。测试用例本身没有报错但响应里总是少了一个字段后来比对网络抓包才发现对端返回是按不同协议走的。这种问题光靠测试用例不够必须结合网络层面的工具一起看。所以我的建议是SAP ABAP调RESTful API不要只盯着代码要把SM59配置、协议细节、对端接口文档这三样东西当成一个整体来对待。代码只是最后一步的执行者前面任何一环出错代码都会表现出各种稀奇古怪的症状。这套测试用例能做的就是把症状清晰地呈现出来帮你更快地找到病根。
阅读完成 · 觉得有帮助?
咨询建站