Now accepting guest editorial from SAP, Oracle, Workday and ServiceNow practitioners.Pitch a guide
The Sign-Off Table

Lesson 3 of 6

Querying: $filter, $select, $expand and paging

10 min

Listen to this lesson

0:00 / 15:44

Most API tests are reads: check that a document exists with the right values, that a list contains what it should, that a filter returns the right subset. Getting the query exactly right is a testing skill in itself, because a wrong query fails quietly: it returns 200 with the wrong data, or with only the first page.

The query options you will use daily

OptionExample (V2, Business Partner)Purpose
$filter$filter=BusinessPartnerCategory eq '2'Organizations only
$filter with and$filter=BusinessPartnerCategory eq '2' and CreationDate ge datetime'2026-01-01T00:00:00'Combine conditions; V2 date literal syntax
$select$select=BusinessPartner,BusinessPartnerFullNameReturn only what the test checks
$expand$expand=to_BusinessPartnerAddressInclude related entities in one call
$orderby$orderby=BusinessPartner descStable order for paging and comparisons
$top / $skip$top=50&$skip=100Client-side paging
Count$inlinecount=allpages (V2) or $count=true (V4)Total number of matches with the page

Query options with Business Partner examples.

V4 adds richer syntax: $filter=contains(BusinessPartnerFullName,'Fleet') where V2 uses substringof('Fleet',BusinessPartnerFullName), and V4 allows options inside an expand, such as $expand=_Item($select=Product,RequestedQuantity). Whether a service supports a function or a nested option depends on the implementation, so try it in the sandbox before writing it into a test.

Literals by type

TypeOData V2 literalOData V4 literal
String'17100001''17100001'
Date and timedatetime'2026-10-14T00:00:00'2026-10-14T00:00:00Z (DateTimeOffset) or 2026-10-14 (Date)
Decimal12000.00M12000.00
GUIDguid'...'The GUID without prefix
Booleantrue / falsetrue / false

Literal syntax changes between versions; a V2 date literal sent to a V4 service fails with 400.

Keys, leading zeros and encoding

  • Leading zeros. Many SAP keys are character fields with leading zeros. In some services the key must be sent with them; others convert. Great Lakes Fleet Services is business partner 17100001, which some interfaces show as 0017100001. Read the metadata and test both forms once.
  • Quotes. String keys and literals go in single quotes; a quote inside a value is doubled.
  • Encoding. Spaces, quotes and special characters must be URL-encoded. Most clients do it; hand-built URLs in scripts often do not.
  • Case. Property names are case-sensitive. businesspartner is not BusinessPartner.

Paging: the quiet failure

Services may limit how many entries they return per request. When they do, the response contains a link to the next page: __next in V2 JSON, @odata.nextLink in V4. A test that counts "all open sales orders for Great Lakes" and reads only the first response is wrong as soon as the data grows. Always request a count, and follow next links until there are none.

A read test follows next links and checks the total against the count before asserting anything.
The Sign-Off Table · thesignofftable.com/academy/sap-tst-211

Testing the filter itself

When the thing under test is a filter (an interface that pulls "orders changed since the last run", or a custom service with a date parameter), apply the boundary-value technique from sap-tst-201. Create or find records just before, exactly on and just after the boundary, and assert which come back. Date filters are the usual culprits: ge versus gt, time zones in V4 DateTimeOffset values, and V2 DateTime values that the backend reads as local time. A delta interface that misses records created at exactly midnight is a real and expensive defect.

$expand versus many calls

A test that needs a sales order and its items can make one call with $expand=to_Item or one call for the header and one per item. The expanded call is faster and is what Fiori apps typically do, so it also tests the path users take. Expanding several levels deep on large documents can be slow or hit service limits, though, so keep expands to what the assertions need.

One expanded call returns header and items together; without $expand the items are a deferred link that needs a second call.
The Sign-Off Table · thesignofftable.com/academy/sap-tst-211

Worked example: Great Lakes orders

In CMQ, Maria's regression set checks that sales order 1000457 for Great Lakes Fleet Services carries 50 pieces of CMC-ALT-100 at 240.00 USD. A precise read is GET /sap/opu/odata/sap/API_SALES_ORDER_SRV/A_SalesOrder('1000457')?$expand=to_Item&$select=SalesOrder,SoldToParty,TotalNetAmount,TransactionCurrency,to_Item/Material,to_Item/RequestedQuantity,to_Item/NetAmount. The test asserts SoldToParty 17100001, TotalNetAmount 12000.00, currency USD, one item with material CMC-ALT-100 and quantity 50. Property names come from the service metadata; verify them for your release before you rely on them.

  1. MetadataConfirm entity, keys, property names and filterable fields
  2. Sandbox trialTry the query shape on sample data
  3. Narrow$select only asserted fields; $orderby for stability
  4. Count and pageRequest a count; follow next links
  5. AssertBusiness values, not only status 200
Building a read test that cannot pass by accident.
The Sign-Off Table · thesignofftable.com/academy/sap-tst-211

What would you do?

A regression test checks that "all Great Lakes Fleet Services orders created this week are in status open". It returns 200 and passes, but Maria knows one order was blocked. The test reads A_SalesOrder with a $filter on SoldToParty and CreationDate. What should you check first?

Key points
  • $filter, $select, $expand, $orderby, $top/$skip and count are the daily toolkit.
  • V2 and V4 differ in functions (substringof versus contains), count and paging links.
  • Watch leading zeros, quoting, encoding and case.
  • Follow __next or @odata.nextLink and compare with the count before asserting.

Check your understanding · not graded

Lesson check

  1. 1. Which V2 option returns the total count with the page?

  2. 2. Where is the next-page link in a V4 JSON response?

  3. 3. Which V4 function matches the V2 substringof?

  4. 4. Why use $select in a read test?

  5. 5. A filter returns 200 and an empty list. What does that prove?