Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Run framework agnostic tests #828

Closed
wants to merge 15 commits into from

Conversation

ainquel
Copy link
Contributor

@ainquel ainquel commented Dec 18, 2018

Hello,

context

a couple weeks ago, I created a branch to add quart support to connexion. I managed to implement the app and api but I struggled when it came time to test my work.
I wasn't familiar at all with the connexion codebase and didn't feel able to add tests.
Also this was frustrating because Flask and Quart follow almost the same API (except async for quart). I just wanted to use all tests that request the tests API via test_client().

I like your idea of API first principle, it makes APIs consistent by taking the time to think. And to write an openapi before starting coding helps to normalize data sent and returned by the API. It is easy to implement well documented API contracts and keep the code clean and business focused.

In the future, I would like to have the possibility to choose any python framework and to use connexion on it.
I think that to test the same way each implementation could help contributors to add more frameworks to connexion, if the contributor only has to implement the interface and see a concrete error if its code is wrong. It would also ensure that whatever server I'm using, the API is consistent and responds the same way.

proposed changes

I've started working on it,

  1. I added an API AbstractApp.test_client() which returns an AbstractClient, each web server has to implement 1 synchronous method to request the API and returns an enhanced version of ConnexionResponse (just added helpers like response.json, response.text).
    For now the API to send request via this AbstractClient respects the client's flask API.
  2. I've changed all tests that were using app.app.test_client() to app.test_client() and all tests passed.
  3. I created the test_client for the AioHttpApp
  4. I am now trying to pass all tests with AioHttpApp, and I encountered this issue. Are you okay if I'm making changes to respect the same return API that flask ?

Are you okay with changes I'm proposing in this pull request ?

The work currently won't pass the CI, I'm creating this pull request to share my idea with you and have your opinion on it

@spec-first spec-first deleted a comment Dec 20, 2018
@spec-first spec-first deleted a comment Dec 20, 2018
@@ -34,8 +38,39 @@ def __init__(self,
content_type=None,
body=None,
headers=None):
if not isinstance(status_code, int) or not (100 <= status_code <= 505):
raise ValueError("{} is not a valid status code".format(status_code))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should connexion really be policing which status codes the user is allowed to send?

@dtkav
Copy link
Collaborator

dtkav commented Jan 8, 2019

Hey @Panpann - I really like the idea of patching up the differences in the API between the different frameworks. I definitely applaud your effort to fix this. I'm also excited about the idea of being compatible with more frameworks like quart.

I started reading through your PR, and it's quite long and included many orthogonal changes.

Can you think about how you might break it up into chunks that all make sense on their own?
For example, large scale whitespace changes should be done separately from changes in functionality.

Some things that could make sense as standalone fixes/improvements:

  • app.app.test_client() -> app.test_client()
  • additional parameterized tests
  • remove werkzeug workaround in test_post_wrong_content_type
  • max character length whitespace fixes

I'd love to review any of these individually, and continue to discuss the broader goal of cleaner abstractions, and supporting more frameworks.

Sorry I wasn't able to get back to you sooner. I was on holiday. Great work!

@Jyhess
Copy link
Contributor

Jyhess commented Jan 10, 2019

I like the idea to support tuple as return values for AioHttp handler, like for flask.
I did the same job in https://github.com/Jyhess/connexion/tree/return_tuple_for_aiohttp and discover your pull request just before submitting mine.
Is it make sense for me to continue on this way as a first step of this bigger work ?

@dtkav
Copy link
Collaborator

dtkav commented Jan 13, 2019

Hey @Panpann and @Jyhess I'm very keep to implement these changes.
As I said, it's ideal to break them up into smaller and more targeted diffs. If you like I can probably help with that at the end of the week, but if you have something ready I'd be happy to review it.

Jyhess pushed a commit to Jyhess/connexion that referenced this pull request Jan 14, 2019
@Jyhess Jyhess mentioned this pull request Jan 14, 2019
@Jyhess
Copy link
Contributor

Jyhess commented Jan 14, 2019

@Panpann @dtkav I let you review #849

Jyhess pushed a commit to Jyhess/connexion that referenced this pull request Jan 14, 2019
@ainquel
Copy link
Contributor Author

ainquel commented Jan 15, 2019

Hi @dtkav

I wasn't available last few days, I know there is a lot of changes in this PR, this is because I'm debugging one test file in tests/api/ folder after another.
One way to ease the review could be to create small PRs when a functionnality emerge from this mess :)

Do you have another solution ?

@dtkav
Copy link
Collaborator

dtkav commented Jan 15, 2019

That sounds great. @Jyhess has already been pulling off smaller chunks too, so looks like there's some good collaboration! I'm excited about the direction this is going.

@ainquel
Copy link
Contributor Author

ainquel commented Jan 16, 2019

OK I pushed my last local changes as a reference.

My first step will be to create the App.test_client for aiohttp and flask.
This is what I've done in this branch but then I ran all tests with flask and aiohttp apps and errors happened..

I will find a way to explicitly set a test as running in FlaskApp or AioHttpApp or both, something like

@pytest.mark.app("FlaskApp")
def test_specific_for_flask():
    pass

This will allow to incrementally run both Flaskapp and AioHttpApp on specific tests covered by thoses small PRs

@spec-first spec-first deleted a comment Jan 16, 2019
@spec-first spec-first deleted a comment Jan 16, 2019
Jyhess pushed a commit to Jyhess/connexion that referenced this pull request Feb 4, 2019
Jyhess pushed a commit to Jyhess/connexion that referenced this pull request Feb 4, 2019
@cognifloyd
Copy link
Contributor

After quart support gets in, I'd love to see sanic join the connexion crew as well :) this is awesome stuff. Is #849 the only break out PR so far?

Jyhess pushed a commit to Jyhess/connexion that referenced this pull request May 7, 2019
cognifloyd pushed a commit to Jyhess/connexion that referenced this pull request Dec 4, 2019
@cognifloyd
Copy link
Contributor

This will need to be rebased, but probably after #849. Some of the commits will drop out as they are fixed or implemented elsewhere.

58e3fbb is implemented in #849 (ready)
8eda5e3 was implemented in #952 (merged)
67e7dac looks like it is no longer needed.

These are moving things to operation validation - Why? does that make sense?
b8d979f I'm not sure how to reimplement this with the new _jsonify_data in abstract, or even if we should.
e488a6f This bit moved to abstract api.

Parts of these are candidates for dedicated coding style PRs:
7c0cdd3 ws: tests.test_json_validateion
9eaadaf ws: tests.fakeapi.hello
1d40dda imports

It looks like the other commits fall into three general categories:

  1. Generalize ConnexionRequest ConnexionResponse to make them useful for testing
  2. Generalize a TestClient
  3. Adjust the tests to use the generic test client and parametrize them to use both implementations

hjacobs pushed a commit that referenced this pull request Dec 11, 2019
* Support aiohttp handlers to return tuples

* Minor update from #828 review

* Factorize more code between Flask and AioHttp response

* Fix CI

* Drop six string types

* Standardize response logging

* Handle one-tuples that only contain data

* clean up a couple of type hint comments

* Add a few more get_response tests

* Adjust _prepare_body interface to simplify improving _serialize_data

Rename _jsonify_data to _serialize_data to make its purpose easier to
understand (this was also known as _cast_body in aiohttp_api).

In exploring how to harmonize json serialization between aiothttp and
flask, we needed to be able to adjust the mimetype from within
_serialize_data. Harmonizing the actual serialization has to wait until
backwards incompatible changes can be made, but we can keep the new
interface, as these functions were introduced in this PR (#849).

* Add deprecation warnings about implicit serialization
@ShadowJonathan
Copy link

Any chance this pull request can be pushed forward?

@RobbeSneyders
Copy link
Member

Closing as this is based on outdated code. Since we're splitting a lot of functionality into framework-agnostic middleware, this will become a lot easier. Would still be interested to review a PR for a connexion test client.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants