-
Notifications
You must be signed in to change notification settings - Fork 62
docs: Add outline of unit testing recommendations #619
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
base: main
Are you sure you want to change the base?
Conversation
Over, looks good, just one overall issue that can be solved mostly by moving stuff around. I do not think we should push anyone toward unittest over pytest for unit tests. The reasons:
That last point is important: unit tests are for developers to develop the code. End users do not need to run unit tests. Once the units work correctly, then you don't need to verify that again. However, I think we should instead emphasize a new category of tests, the one we mentioned (I don't remember your term, "smoke test" is what I thought of). That's a separate category from unit tests, and for that one you don't want dependencies; it's a great place to use unittest. So:
So I'd basically recommend moving the unittest parts to a new section about smoke tests; the actual content is fine, just I think it needs reordering. (And replace "smoke" with whatever you mentioned today, I've just forgotten what it was. Validity? Verification? ...) |
The only other thing I'd add is maybe a mention somewhere that this is guidelines for good design, but even poorly structured tests are better than no tests. I don't want to discourage someone from writing tests, but instead give them ways to improve their test design if they want to, and help as users scale up to larger projects. |
Here's an example I added to boost-histogram: scikit-hep/boost-histogram#1022 |
The term I use is "Diagnostic Tests", FFR: |
f932498
to
8af993d
Compare
ae6f62e
to
e38e5c7
Compare
Let me know when it's ready for another review! |
4999dcf
to
b9158de
Compare
Thank you for your patience @henryiii, I have one question we could use help with. |
4c19f10
to
6b4e093
Compare
6b30db4
to
1e10a46
Compare
@henryiii this is ready for another review |
1e10a46
to
5716e45
Compare
For now, keeping it on one page makes sense, as there's a discussion on restructuring going on anyway. I'm about to leave for a week-long vacation, so might not be able to get to this. I've got a couple of sections I'd like to work on a bit, and I'd like to compare this with the current pytest page to see if I can reduce overlap or cross-link. One solution could be to merge in more-or-less the current state (I could do a quick review), then I could make PRs to update it later. Or we can wait till I get back - do you have a preference? |
This went through several rounds of proof-reading and revision, so I'm happy to merge it, and continue working on it in the future through new Issues/PRs. I don't mind if you prefer to wait and do a more thorough final review when you get back. Whatever works best for you. |
Co-authored-by: Lauren Moore <[email protected]>
5716e45
to
34d10c3
Compare
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.
Added a few comments; some of it is fine for followup. Just the key stuff like renaming the src.
import matters. Take it out of draft when you are ready!
``` | ||
|
||
```python | ||
from src.lib import say_hello, dangerous_sideffects |
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.
Please don't name anything src. That's a folder name we use specially intending it to never show up in imports. It's not good to make people think it's okay to write "src" in their import statements. Select some other name. :)
namespace into an imported dependency's namespace, like so: | ||
|
||
```python | ||
def test_(mocker): |
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 can do it in a followup, but we should use monkeypatch where possible. Mocking should be used if you have to make a thing to monkeypatch in, but the patching process is much better with monkeypatch.
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.
Also, I like keeping test_func
or some similar name, I've seen people actually write def test(...)
tests, which is really annoying, as it's not usually clear what it's testing, and it's hard to select with -k
.
Consider the benefits of refactoring your imports like so: | ||
|
||
```python | ||
from numpy import sum as np_sum, Array as NpArray |
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.
This should not be done except very special circumstances. Normally, you should monkeypatch the import and not use import ... from
. There are some circumstances where you need to avoid this sort of import, like to avoid circular import issues. You can't import a unittest TestCase this way or it triggers the runner, in fact! Some projects strongly recommend avoiding from
imports. There are special cases where you need this, for example if you need to patch in something but you it must only affect the local code, so it's good as a tip, but not the general recommendation for design. Especially renaming imports like this. :) Happy to address in a followup, though.
Also full imports are completely unambiguous. :)
|
||
We recommend using Pytest for running tests in your development environments. To | ||
run unit tests in your source folder, from your package root, use | ||
`pytest {path/to/source}`. To run tests from an installed package (outside of |
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.
Might be worth mentioning that this can be set in pyproject.toml, as most test suites allow you to just run pytest
without args. If you do, this can be just the unit tests, requiring a user to specify other tests explicitly. We actually ran into a problem with specifying both paths in cibuildwheel; don't remember exactly what it was, but it is better to just specify the unit tests folder as the default I think.
|
||
## Testing Edgecases | ||
|
||
While writing unit tests, you may be tempted to test edgecases. You may have a |
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'd clarify that this is about long, trick things to test. Generally, you should always test edge cases like minimum, maximum, None, etc. I think the problem is the name - "edge cases" I think implies the wrong thing.
`import numpy as np`. However, as we develop our unit tests, this can cause | ||
difficulty with mocking, and complicate refactoring. | ||
|
||
To patch out numpy.sum in your test, you either need to patch the _global_ numpy |
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.
To patch out numpy.sum in your test, you either need to patch the _global_ numpy | |
To patch out `numpy.sum` in your test, you either need to patch the _global_ numpy |
difficulty with mocking, and complicate refactoring. | ||
|
||
To patch out numpy.sum in your test, you either need to patch the _global_ numpy | ||
module, which can have unintended side-effects, or specifically patch numpy.sum |
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.
FYI, monkey patching is isolated to just the function (and can even be reduced to just a line) so it normally works fine. It's only in special cases where something gets called multiple times and you only want to change your usage, which is pretty rare.
- Test files should be named `test_{file under test}.py`, so that test runners | ||
can find them easily. | ||
|
||
- test\_.py files should match your source files (file-under-test) one-to-one, |
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.
- test\_.py files should match your source files (file-under-test) one-to-one, | |
- `test_*.py` files should match your source files (file-under-test) one-to-one, |
This PR contains an outline of a proposed Development Guide / Principles / Unit Testing Recommendations section.
I am opening this PR now to allow discussion of the outline, and key-points, before fleshing out the complete page.
📚 Documentation preview 📚: https://scientific-python-cookie--619.org.readthedocs.build/