robotframework icon indicating copy to clipboard operation
robotframework copied to clipboard

Collections: Support ignoring order in values when comparing dictionaries

Open newedgex opened this issue 1 year ago • 5 comments

Currently https://github.com/robotframework/robotframework/blob/master/src/robot/libraries/Collections.py#L839 we are missing out on ignore_order already done for

    def lists_should_be_equal(self, list1, list2, msg=None, values=True,
                              names=None, ignore_order=False, ignore_case=False):

Ref : https://github.com/ngoan1608/robotframework/commit/1d852208255d1245cce33a34c700478455cc7298#diff-0bb57664ba32d4e2b91c6435cf0b867d630c2db282af83004073303030775bd1

Adding as well ignore_key would be much appreciated since we might deal with ID's and timestamps, and in such cases those keys are to be ignored.

newedgex avatar Jan 09 '24 09:01 newedgex

Although Python nowadays preserves dictionary order, the order doesn't generally affect equality and it shouldn't matter with this keyword either. Do you have a concrete example where ignore_order would be needed?

ignore_keys could be added for consistency with Dictionaries Should Be Equal. I consider it very low priority, though, because with Dictionary Should Contain Sub Dictionary the second dictionary doesn't need to contain all keys that the first dictionary contain in the first place. If there's a key you'd like to ignore, you can easily remove it from the second dictionary already now. Alternatively you can use Dictionaries Should Be Equal that already supports ignore_keys. Do you have examples where ignore_keys with Dictionary Should Contain Sub Dictionary would be convenient?

If we agree adding this functionality would be a good idea, can you provide a pull request implementing it?

pekkaklarck avatar Jan 09 '24 14:01 pekkaklarck

In my case I'm querying an API (fastapi) where item order (in case of lists) and key order (in case of dictionaries) is not enforced.

The DUT , after a reboot will give a different order making the test very brittle and causing it to fail. Example :

Get the safesearch created profile by ID
    API GET dictionary request comparing the response  ${ROOT}    webfilter/api/v1/config/profiles/1    ${POST_PROFILE_SAFESEARCH_RESPONSE}
API GET dictionary request comparing the response
    [Documentation]       It makes a GET request to the specific baseurl and endpoint and compares the API response (should be a dictionary!) 
    ...                   to another dictionary defined in (json_response). 
    [Arguments]    ${baseurl}    ${endpoint}    ${json_response}
    ${url}=    Set Variable    ${baseurl}${endpoint}
    ${response}=    GET On Session   url=${url}    alias=get_login 
    ${API_SERVER_RESPONSE_HEADERS}=    Create Dictionary    Server    uvicorn    content-type    application/json    X-Robots-Tag    noindex, nofollow    Connection    Keep-Alive
    Dictionary Should Contain Sub Dictionary    ${response.headers}    ${API_SERVER_RESPONSE_HEADERS}
    Should Be Equal As Strings    ${response.status_code}    200
    Run Keyword If    '${response.status_code}' != '200'    Log Variables
    ${evaluated_response}    Evaluate    json.loads('''${json_response}''')
    Dictionary Should Contain Sub Dictionary    ${response.json()}    ${evaluated_response}
    Should Be Equal As Strings    ${response.reason}    OK

This is the error that I'm getting :

Get the safesearch created profile by ID                              | FAIL |
Following keys have different values:
Key safesearch: ['DUCKDUCKGO', 'GOOGLE', 'BING'] != ['DUCKDUCKGO', 'BING', 'GOOGLE']

This is the request / var ${POST_PROFILE_SAFESEARCH_RESPONSE}

 {"id":null,"antivirus":true,"blacklist":[],"allowed_domains":[],"blocked_domains":[],"safesearch":["GOOGLE","DUCKDUCKGO","BING"],"name":"safesearch"}

So basically the API might return different orders for the search engines.

In this case ignore_order would be needed.

If we agree adding this functionality would be a good idea, can you provide a pull request implementing it?

I would love to and would have already done it but I don't have the right confidence with python.

newedgex avatar Jan 10 '24 10:01 newedgex

So you don't want to ignore order of dictionary keys but instead want to ignore order in their values? I guess something like ignore_value_order could be added, but I don't consider it too high priority. It should be added to Dictionaries Should Be Equal as well.

Is there a reason you couldn't get those values and use Lists Should Be Equal that already supports ignoring order?

pekkaklarck avatar Jan 10 '24 12:01 pekkaklarck

So you don't want to ignore order of dictionary keys but instead want to ignore order in their values?

Exactly, and yes ignore_value_order would be appropriate. As for Lists Should Be Equal I can confirm that it works (even without adding ignore_order=True and it seems that by default it ignores the order of the values which in my case is appreciated. Thanks allot for the quick replies and even more for providing a tool like Robot Framewok, I truly mean it.

newedgex avatar Jan 10 '24 14:01 newedgex