internap / python-mockserver-friendly-client

Friendly python client to James D. Bloom's awesome MockServer
Apache License 2.0
14 stars 12 forks source link

Feature for API retrieve #22

Closed true3HAK closed 4 years ago

true3HAK commented 5 years ago

Added a feature for API of mockserver to retrieve logged objects.

I've also seen a feature request for this thing in the repo.

lindycoder commented 5 years ago

Adding features require tests

lindycoder commented 5 years ago

Additionally, tests will provide the use case and how to use it

Thank you

true3HAK commented 5 years ago

@lindycoder Sure, I will add. I'm not really familiar with Nosetest, however :( I used to pytest, so it'll take some time. Thank you!

lindycoder commented 5 years ago

Nosetest has the exact same contract as unittest the built in python test framework.

I was thinking about this feature and how to make it clean in the code, because the goal of this library is to present a simple an inuitive interface. If you want pure api implementation there's one coming that implements open api by James Bloom himself.

So i wonder how you plan on using this feature but here's how i would probably want to it for inspecting generated content for example:

expectation = self.client.expect(
    request(path="/path"),
    response(code=200),
    times(1)
)

request.get("...", json={"generated": "value"})

self.assertEquals(expectation.calls(0).json, {"generated": "value"})

This is a rough draft but that's kind of the idea

So the expect() method could return an expectation object that remembers the request() given and can use it to call the /retrieve. According to the https://app.swaggerhub.com/apis/jamesdbloom/mock-server-openapi/5.4.x#/control/put_retrieve it uses the same request matcher has the httpRequest param of the /expectations

I'm open to suggestions

true3HAK commented 5 years ago

Thanks, I think, I got the idea :) BTW, that's how I use the forked version:

  1. Setting up the expectation, so mockserver doesn't answer with 400
        # Expect items to be sent
        mockserver_client.expect(
            request=request(method='PUT', path=r"\/procurements\/\d+\/phase1\/items"),
            response=response(code=200)
        )
  2. Then, retrieve caught request:
        sp_notification_phase1_items = mockserver_client.retrieve(
            obj_type=RetrievedObjects.requests.value,
            response_format=RetrievedFormat.json.value,
            body={'path': r"\/procurements\/\d+\/phase1\/items"}
        )
        decoded_notification_phase1_items = json.loads(sp_notification_phase1_items.content.decode())
  3. Finally, check retrieved objects
    from hamcrest import assert_that, is_not, empty
    assert_that(decoded_notification_phase1_items, is_not(empty()))

    The biggest problem here, is that retrieve method doesn't comply to signatures of other methods (like stub, verify), 'cause is works different – in my case I don't verify anything server-side (mocker-side, ofc), just "drag out" and then check in a test.

I have no strong idea if this is a problem or not.

lindycoder commented 5 years ago

According the api, they do that have the same signature in the content you post

check this out : https://imgur.com/a/raCeAg1

So when doing a .expect you can save the request= param and us it in the retrive without having to copy the request content

Also looking at your example it would probably make sense to have

class Expectation:
    def __init__(request):  
        self.request = request
    def get_requests() -> Retrieval()
        return Retrieval("requests", self.request)

class Retrieval:
    def __init__(type, request):
         self.type = type
         self.request = request

    def json() -> List[dict]:
         requests.put("/retrive....&type=self.type&format=json)

Then your example would look like:

expectation = mockserver_client.expect(
            request=request(method='PUT', path=r"\/procurements\/\d+\/phase1\/items"),
            response=response(code=200)
        )

requests = expectation.get_requests().json()

assert_that(requests, is_not(empty()))
true3HAK commented 5 years ago

I need to think a bit more. There are some points about potential Expectation object.

If you want OOP here instead of imperative procedural paradigm, we need to pack everything to appropriate objects.

lindycoder commented 5 years ago

Reading the /retrieve api i realised that only requests fetching is related to expectation, logs or record expectation retrieval should be topic handled on its own given the use case.

If the format should change here i would create a .java() but i don't see how this could be beneficial in python so maybe we don't even need the .json and the expectation could have a @property .requests that gets the requests.

Now for your use case, i understand the path is a regex but the example you provided uses the same regex on the retrieval.

The goal of this lib is to make beautiful elegant and readable tests... so when you way we need an OOP paradygm instead of procedural, i say : write the tests how you want it to look, then make it work. That's what generated the current structure, it doesn't need to be more complicated.

So if i may suggest : Take your actual use case, make a test for it, make it pass and we'll see later if we have other needs.

agility :P

true3HAK commented 5 years ago

Ok, I got it, thank you, Martin :) I'll find some time on the weekend and will finish this then.

true3HAK commented 4 years ago

Closing this due to #26 actually seems much better, and I still can't get over myself to write tests for Nose :(