pact-foundation / pact-jvm

JVM version of Pact. Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.
https://docs.pact.io
Apache License 2.0
1.08k stars 479 forks source link

Object can be null or have value #319

Closed sanketi closed 7 years ago

sanketi commented 8 years ago

Hi, This is a question. What is the best way for me to define something like below in Pact Fragment -

"Certification": {
    "Java": {
        "Version": "1.7",
        "Year": "2015" 
    },
    "Scala": null,
    "C#": null,
    "C": null,
}

So basically inside Certification object, each object can be null or have some additional fields.

What would be a good way to do this so it accepts values or null?

alonpeer commented 8 years ago

I assume you wish to address the response part of the fragment. If you wish to match a JSON which has an unknown (zero or more) objects under "Certification", with the key being any string (Java, Scala etc.), and the value being any object, then I can't think of a way of achieving this with Pact. However, I would suggest changing your schema to something like:

"Certification": [
  {
    "language": "Java",
    "version": "1.7",
    "year": 2015
  },  
  {
    "language": "Scala",
    "version": "2.11",
    "year": 2016
  }
]

Then you can use DslPart to build a body matcher like so:

  val body = new PactDslJsonBody()
    .minArrayLike("Certification", 1, 1)
      .stringType("language")
      .stringType("version")
      .integerType("year")
      .closeObject()
    .closeArray()

This will eventually generate a pact file similar to this:

{
    "provider": {
        "name": "My Provider"
    },
    "consumer": {
        "name": "My Consumer"
    },
    "interactions": [
        {
            "description": "a request for foo with a body",
            "request": {
                "method": "GET",
                "path": "/foo",
                "body": ""
            },
            "response": {
                "status": 200,
                "body": {
                    "Certification": [
                        {
                            "language": "MpTNFzmpGufqeCoKZsJp",
                            "version": "wyVICggKfSytgwObduad",
                            "year": 604416835
                        },
                        {
                            "language": "MpTNFzmpGufqeCoKZsJp",
                            "version": "wyVICggKfSytgwObduad",
                            "year": 604416835
                        },
                        {
                            "language": "MpTNFzmpGufqeCoKZsJp",
                            "version": "wyVICggKfSytgwObduad",
                            "year": 604416835
                        }
                    ]
                },
                "matchingRules": {
                    "$.body.Certification": {
                        "min": 1,
                        "match": "type"
                    },
                    "$.body.Certification[*].language": {
                        "match": "type"
                    },
                    "$.body.Certification[*].year": {
                        "match": "integer"
                    },
                    "$.body.Certification[*].version": {
                        "match": "type"
                    }
                }
            }
        }
    ],
    "metadata": {
        "pact-specification": {
            "version": "2.0.0"
        },
        "pact-jvm": {
            "version": ""
        }
    }
}
uglyog commented 8 years ago

@alonpeer has described a elegant solution if you are able to change the format of the JSON.

As to optional values, we don't really support them (the idea is to leave optional things out and only include the important things in the contract. Also see https://docs.pact.io/faq/#why-is-there-no-support-for-specifying-optional-attributes)

There is a specification change proposed (https://github.com/pact-foundation/pact-specification/tree/version-4#additional-matchers) to introduce an or condition with additional matchers, but it is a work in progress.

sanketi commented 7 years ago

Thank you @alonpeer for the detailed example. At this point unfortunately I cant change the schema to make this change.

@uglyog : thank you for your comment as well. So I will exclude these optional attributes from my test.

maxant commented 7 years ago

How about this contract:

The customer may use pact-jvm for free as it is open source. Conditionally, if it were to support optional attributes, the customer must pay $1m.

But hey, optional stuff isn't important, so let's just ignore it and let the customer use it for free ;-)

bengro commented 6 years ago

This is a real shame to not support conditional parts in responses. Is there no solution/workaround to this problem still?

Given I have different types of objects:

[
  {
    type: "TYPE_A"
  },
  {
    type: "TYPE_B"
  }
]

I would like to be able to write a matcher, which only focuses on TYPE_A, hence making my matching rules conditional on the type.

Given the schema-less nature of JSON, this should definitely be supported.

mefellows commented 3 months ago

I stumbled across this old issue which seems to be linked from a few places so I thought a quick update is warranted.

First up, support for your use case @bengro is part of the ArrayContains matcher. It is possible, but is different than what the OP is asking for which is support for conditional/optional attributes which Pact does support, it's just that each variant needs individual testing.

Given the schema-less nature of JSON, this should definitely be supported.

This statement is orthogonal to what we're doing here. Pact is a specification-by-example framework whose job is to prevent things from breaking. Whether or not a schema exists is irrelevant, as the presence of a schema itself doesn't guarantee behaviour.

For more on this topic see https://pactflow.io/blog/schemas-are-not-contracts/ and the Pact justification here: https://docs.pact.io/faq/#why-is-there-no-support-for-specifying-optional-attributes.