-
Notifications
You must be signed in to change notification settings - Fork 3
Add doc on testing #23
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
Conversation
Signed-off-by: i2y <[email protected]>
Signed-off-by: i2y <[email protected]>
1f96395
to
a29ee46
Compare
Signed-off-by: i2y <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good to me — we're being pretty pytest
-specific in this doc, but I think that's reasonable given how much the ecosystem seems to prefer it over unittest
docs/testing.md
Outdated
method_name = ctx.method().name | ||
self.requests.append(method_name) | ||
return method_name | ||
|
||
async def on_end(self, token, ctx): | ||
# token is the value returned from on_start | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to return the method_name
/ have the comment if we just check self.requests
? up to you if you want to keep this in (similar comment for WSGI
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that along the lines of above comments, it's best if we show how to test something real since users will be reading this for advice on that. This LoggingInterceptor
doesn't actually log - to make it a testable real interceptor, we'd probably pass in a Logger
and mock it or similar. Alternatively, auth interceptor may be easier to demonstrate here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree pytest is a de-facto standard and don't think we need to avoid it in the examples. However, I've left some comments in that we probably should minimize actually documenting pytest itself - one reason is that it does end up looking like we basically require pytest for testing connect code which isn't the case.
Perhaps in the very first section Recommended approach: In-memory testing
we can add a code snippet with no test infrastructure at all, documenting the most important point of the doc, using the httpx transports to connect a client and server without an app server.
import httpx
from greet_connect import GreetASGIApplication
from server import Greeter
app = GreetASGIApplication(Greeter())
async with httpx.AsyncClient(
transport=httpx.ASGITransport(app=app),
base_url="http://test"
) as session:
client = GreetServiceClient("http://test", session=session) # URL is ignored
response = client.greet(GreetRequest(name="Alice"))
print(response.greeting)
Then add some examples wiring it up to pytest, including some basic example of fixture. But I'd recommend
- Keeping "general usage of connect" as much as possible in other docs (we can add what's missing there that we have in this PR)
- Minimizing "general usage of pytest" as much as possible. In fact, instead of having many examples of pytest usage, we could instead have our basic example covered in two forms, pytest and unittest
How does that sound?
docs/testing.md
Outdated
from greet.v1.greet_connect import GreetService, GreetServiceASGIApplication, GreetServiceClient | ||
from greet.v1.greet_pb2 import GreetRequest, GreetResponse | ||
|
||
class TestGreetService(GreetService): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the section about testing services, I think we wouldn't have names like TestService
- we should be testing the real code here.
I think we can do something like
Testing the service we created in the [Getting Started](./getting-started) guide may look like this:
from greet_connect import GreetASGIApplication
from server import Greeter
any real test will be importing the service, not having it defined in the same file
docs/testing.md
Outdated
@pytest.mark.asyncio | ||
async def test_greet(): | ||
# Create the ASGI application | ||
app = GreetServiceASGIApplication(TestGreetService()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't show it clearly with our example services, we could consider something like # Initialize your service with mock dependencies as needed
. Though maybe we can leave "general testing" advice out of the doc
docs/testing.md
Outdated
assert response.greeting == "Hello, Alice!" | ||
|
||
@pytest.mark.asyncio | ||
async def test_greet_multiple_names(greet_client): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There doesn't seem to be multiple names. How about empty_name
and use empty string?
docs/testing.md
Outdated
- Follows pytest best practices | ||
- Matches the pattern used in connect-python's own test suite | ||
|
||
### Testing error handling |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After writing below comments, I also thought this overlaps well with errors.md.
|
||
@pytest.mark.asyncio | ||
async def test_client_error_handling(): | ||
class TestGreetService(GreetService): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth something like # It can be convenient to use a mock such as with pytest mocker
?
docs/testing.md
Outdated
method_name = ctx.method().name | ||
self.requests.append(method_name) | ||
return method_name | ||
|
||
async def on_end(self, token, ctx): | ||
# token is the value returned from on_start | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that along the lines of above comments, it's best if we show how to test something real since users will be reading this for advice on that. This LoggingInterceptor
doesn't actually log - to make it a testable real interceptor, we'd probably pass in a Logger
and mock it or similar. Alternatively, auth interceptor may be easier to demonstrate here
docs/testing.md
Outdated
|
||
### Project structure | ||
|
||
Organize your tests in a `test/` directory at the root of your project: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this is something I forgot when reorganizing for this repo - AFAIK, tests
is the default for pytest and doesn't require configuration, that's what we should use here.
That being said, this whole section seems more like docs on how to use pytest, rather than testing connect. Maybe we can remove it?
docs/testing.md
Outdated
async def greet(self, request, ctx): | ||
# Check for authorization header | ||
auth = ctx.request_headers().get("authorization") | ||
if not auth or not auth.startswith("Bearer "): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we update the interceptor example, we can remove this one, which is good since I think it's very far from any real life service
Co-authored-by: Anuraag (Rag) Agrawal <[email protected]> Signed-off-by: Yasushi Itoh <[email protected]>
Co-authored-by: Anuraag (Rag) Agrawal <[email protected]> Signed-off-by: Yasushi Itoh <[email protected]>
Signed-off-by: i2y <[email protected]>
Signed-off-by: i2y <[email protected]>
Signed-off-by: i2y <[email protected]>
Signed-off-by: i2y <[email protected]>
Signed-off-by: i2y <[email protected]>
@anuraaga @stefanvanburen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks much nicer, thanks!
docs/testing.md
Outdated
|
||
## Setup | ||
|
||
Install httpx for testing connect-python services and clients: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if we need this, won't it always be installed as a dependency of connect-python?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching that! Fixed in 58d6653.
Signed-off-by: i2y <[email protected]>
This PR adds comprehensive testing guide with ASGI/WSGI examples for services, clients, and interceptors.