#unit-testing
1 messages Β· Page 9 of 1
I'm talking about having an __init__ function
if you have an init, all the test methods require an instance of the object anyway
so init has to be called
oh. again, i think that was done to avoid compatibility issues with existing established uses of __init__ in test classes
it's not that you can't do those things in a fixture, it's that people were doing them in __init__. you'd have to dig around for the actual explanation.
right. I guess that was likely reasonable at the time but it ends up making this feel pretty unnatural
i agree that it would be convenient if __init__ was treated like an always-available autouse fixture
To be quite honest if I was writing these tests from scratch I would do the thing I mentioned above
this thing?
yeah
Okay, good to know I'm not far off
The advantage there is that it separates which tests are using the fixture, from other things you might want to achieve by organizing the tests into a class
like, for example, we often use skip_if at class level
right. you can apply other marks at the class level too if you want (e.g. to mark a class as a regression test, as per our discussion the other day)
if you organize it this way, you can for example put a skip_if on all 10 of your tests at once, but only 5 of them actually need the fixture
the funny thing is that in the end the failing test case wasn't where I thought so I'm just leaving this file alone π
thsi file just had a warning
but now I'll probably use that modify collection hook.
I have this regression path thing, which some tests are now going to accept as a fixture, and they'll be auto skipped if the regression path is not passed.
but some tests don't actually need the regression path itself, but I still want to only run them in "regression mode"
This could be achieved by having them still take the reg_path fixture, but it would be unused. So I'm thinking that a decorator is a better approach?
i still encourage using a mark to indicate which tests are regression tests
but yes, i think it makes sense to expose a regression_path fixture
I already have a regression_path fixture
I'm not sure why I'd need both a mark and accepting the fixture; that seems redundant?
This is for cases where the fixture isn't actually needed, but I still want to only run it in regression mode
@pytest.mark.regress would work I guess
and then modify in collections
one thing I really dont' like here is that if you make a typo, then it just silently works incorrectly
whereas, if you write a test that asks for a non-existent fixture it will fail
Turn on strict mode for marks. HIGHLY recommended
imo it should be the default, but I guess there would be massive backwards compatibility breakage if they did that
that's what i had in mind, yes. i suggested it because it sounded like you want to separate "all regression tests" from "regression tests that use the data path"
great callout thank you, i always forget to do that in a project
Right, I do, but for tests that actually use the regression path, I feel like it's already quite separate
def some_test(reg_path: Path):
...
it's already clear that this is a regression test
I mean I considered just doing it that way even if reg_path is unused but I figured that people would be unhappy that their linters complain and so on
that's fantastic, thanks for pointing that out!
Feel much better about using that now
Yea. It's the difference between madness and sanity π€£
ohh i see. in the modifyitems hook you can attach the regress mark to any test that uses the reg_path fixture, if you want.
i think that was in one of the SO posts i shared the other day
I don't need to do that I think because the reg_path fixture already calls pytest.skip
is there a way to make pytest_collection_modifyitems query another fixture?
You mean like make some items implicitly depend on a fixture?
well, the docs give this example
def pytest_collection_modifyitems(config, items):
if config.getoption("--runslow"):
# --runslow given in cli: do not skip slow tests
return
skip_slow = pytest.mark.skip(reason="need --runslow option to run")
for item in items:
if "slow" in item.keywords:
item.add_marker(skip_slow)
that's fine for me; but the catch is that what I depend on is not a "simple" command line option
rather, I have a fixture that figures out the regression path, based on two command line options, and some logic
@pytest.fixture(scope="session")
def reg_path(request) -> Path:
explicit_reg_str = request.config.getoption("--regression-path")
reg_env_bool = request.config.getoption("--regress")
... logic
you can check which fixtures have been requested for an item, yes
No, that's not what I want
in an ideal world, I would have
def pytest_collection_modifyitems(config, items, reg_path)
or something like that
I want pytest_collection_modifyitems to get the value of the fixture
it seems like you can do this from the request argument
def pytest_collection_modifyitems(config, items):
reg_path_value = config.getoption("--regression-path")
...
like that?
request.getfixturevalue('load_data')
that's not the value of the fixture?
that's just th ecommand line argument
oh. i see.
wait, why do you want that?
i don't think that's possible from modifyitems. the fixture hasn't actually been run at that point afaik.
because, there's two different optiosn that can cause me to run in "regression mode" ?
yeah but why are you checking the fixture value here?
I want to filter out any tests marked as "regress", if I'm not running in regression mode
the logic that figures out whether I'm in regression mode, is effectively already there in the reg_path fixture
the best thing would be to ask for that fixture's value
i'm still not understanding why you need modifyitems here
i think you do need to separate the --regress and --regression-path logic though
to potentially skip any tests marked as @pytest.mark.regress
I mean yeah, the way thigns are headed I'm going to have to factor it out into a separate function, that gets called twice
pretty lame
@pytest.fixture(scope="session")
def reg_path(config) -> Path:
...
since my reg_path fixture only uses the config field of request, I think I can just refactor it like this
and then have pytest_collection_modifyitems(config, items) call reg_path(config)
it's not really a big deal but just kind of silly that the logic is running twice
do you see a better way to do it?
I see a lot of comments online about people not understanding why hooks cannot simply take fixtures directly the same way tests can
i think so... hang on
it's because fixtures run after collection
build graph first, then run it
fixtures run as-needed
I mean, you're describing how it currently works, yes π
but that's the answer why
you'd be asking pytest to fundamentally change its design and execution model, in order to do something that you probably shouldn't do anyway
because it's a good design to keep collection and execution separate π
it would be so much harder to reason about otherwise. not to mention much harder to debug
I mean the issue is that arguably, it's a hack for reg_path to be a "fixture" to start with
it's not a fixture in the proper sense
it should be part of the config
it is part of the config. it's just that you can't access the config from inside a test (afaik)
so maybe that might be an unnecessary limitation
okay, then obviously there is something flawed in the design here?
I don't think there's anything unusual about my use case, basically
from collections.abc import Sequence
from pathlib import Path
import pytest
def pytest_collection_modifyitems(config: pytest.Config, items: Sequence[pytest.Item]) -> None:
# All tests using the "reg_path" fixture should also be marked with "regress".
mark_regress = pytest.mark.regress()
for item in items:
if "reg_path" in test_item.fixturenames:
item.add_marker(mark_regress)
# Tests marked with "regress" should not run unless the --regress option is used.
skip_regress = not config.getoption("--regress", default=False)
if skip_regress:
mark_skip_regress = pytest.mark.skip(reason="Use --regress option to run regression tests.")
for item in items:
if "regress" in item.keywords:
item.add_marker(mark_skip_regress)
@pytest.fixture
def reg_path(config: pytest.Config) -> Path:
return Path(config.getoption("--regress-path", default=False))
what about something like that?
this is fundamentally different to before
this is how i'd attempt to implement what i understood your goal to be
if --regress is specified then reg_path will attempt to get the path from an environment variable
oh right, i missed that detail
but I mean, it seems like the import bit is just changing reg_path to take a config
and then calling reg_path from pytest_collection_modifyitems
import os
from collections.abc import Sequence
from pathlib import Path
import pytest
def pytest_collection_modifyitems(config: pytest.Config, items: Sequence[pytest.Item]) -> None:
# All tests using the "reg_path" fixture should also be marked with "regress".
mark_regress = pytest.mark.regress()
for item in items:
if "reg_path" in test_item.fixturenames:
item.add_marker(mark_regress)
# Tests marked with "regress" should not run unless the --regress option is used.
skip_regress = not config.getoption("--regress", default=False)
if skip_regress:
mark_skip_regress = pytest.mark.skip(reason="Use --regress option to run regression tests.")
for item in items:
if "regress" in item.keywords:
item.add_marker(mark_skip_regress)
@pytest.fixture
def reg_path(request: pytest.FixtureRequest) -> Path:
config = request.config
path_cmd = config.getoption("--regress-path", default=None)
path_env = os.environ.get("TEST_REGRESS_PATH", None)
path_setting = path_cmd or path_env
if path_setting is None:
pytest.skip("TEST_REGRESS_PATH and --regress-path are unset, cannot run.")
else:
return Path(path_setting)
and wait, i might need request.config
let me check the docs
this is of course untested and off-the-cuff
yep, hang on
this is still not correct...
edited
if --regress-path is specified then tests marked regress should still be run
okay then. you get the point though, right?
I mean, yeah, I get that I have the option to just duplicate the logic
just change skip_regress in the modifyitems hook to check for the presence of either options
why is that better than calling reg_path from pytest_collection_modifyitems?
(fwiw i don't think this is actually a very common use case)
the reason is that it's not possible, and the data/executino model is what it is.
def pytest_collection_modifyitems(config, items):
try:
reg_path(config)
return # if no exception, then that means we have a regression path
except:
pass
huh?
what's not possible?
manually calling the fixture function? anything is possible. but the arg needs to be a FixtureRequest not a Config, i made a mistake in my code above
you could of course have a common function for that
ah, I thought you could just have it take a config and it would work
yeah, then I have to factor out into a common function
pretty lame
i actually think a common function might be worse, just from a software design perspective. because now you're running that skip check in 2 different places.
well hang on, let me see
I don't follow that part
import os
from collections.abc import Sequence
from pathlib import Path
import pytest
def _get_reg_path(config: pytest.Config) -> Path | None:
path_cmd = config.getoption("--regress-path", default=None)
path_env = os.environ.get("TEST_REGRESS_PATH", None)
return path_cmd or path_env
def pytest_collection_modifyitems(config: pytest.Config, items: Sequence[pytest.Item]) -> None:
# All tests using the "reg_path" fixture should also be marked with "regress".
mark_regress = pytest.mark.regress()
for item in items:
if "reg_path" in test_item.fixturenames:
item.add_marker(mark_regress)
# Tests marked with "regress" should not run unless the --regress option is used.
skip_regress = not (
config.getoption("--regress", default=False) or _get_reg_path(config)
)
if skip_regress:
mark_skip_regress = pytest.mark.skip(reason="Use --regress option to run regression tests.")
for item in items:
if "regress" in item.keywords:
item.add_marker(mark_skip_regress)
@pytest.fixture
def reg_path(request: pytest.FixtureRequest) -> Path:
path_setting = _get_reg_path(request.config)
if path_setting is None:
pytest.skip("TEST_REGRESS_PATH and --regress-path are unset, cannot run.")
else:
return Path(path_setting)
maybe like that?
I guess I cannot vouch for how common this is in raw numbers. but it seems like a very vanilla use case? Making the value of a command line argument available to unit tests
i don't think a lot of people use custom CLI options other than plugin authors
in part because it's kind of clunky...
Yeah, I mean pytest makes some things here a lot more painful than they need to be
the fact that I even need to write custom logic for this extremely simple case is silly; almost any command line argument parser would let this be specified declaratively
after working through this with you, i'll grant that the CLI arg parser is somewhere between "underdeveloped" and "shitty"
fair enough
I appreciate the validation π
anyhow the final solution won't be so bad
I trust you to wrestle this test suite into submission
yeah, you gotta do what you gotta do
Half the reason I'm pushing this stuff in is so that each time my the test suit fails because my coworker has pushed a unit test that expects a random file to be present
or a random computer to be accessible
I'll just slap reg_path or @pytest.mark.regress` on it
hopefully he actually puts the files in our regression repo and rewrites the test to access the file from there
I've a question.
As a developer, is it recommended to write unit test for all python codes? Or just for APIs?
It depends upon the confidence for accuracy of code and functionalities.
pytest
There is no single right answer. You must learn through bitter experience what the right tradeoff is. Like everything else :P
oh, and beware false prophets who give simple answers
I guess that follows from my previous statement, but still
what's the equivalent to unittest.mock.patch in pytest?
i prefer to use the same unittest.mock.patch π
heh. thats valid I suppose. I was thinking if I'm doing this anyway I may as well get rid of all the unittest stuff, but no harm letting this stay I suppose
though while googling I've already seen questions about somewhat tricky interactions between patch and fixtures
a lot of magic works fine when it's the only magic but when you have two magical things acting on the same function it can get hairy π
that's why I suppose I was hoping for a pytest solution
unittest patch never failed me as context manager
with patch.object(instance, smth=:
or
with patch.multiple ^_^
the code in question actually just uses it as a decorator
@patch("foo.bar.func_name")
def test_whatever(func_name):
sounds magical π
indeed
The closest equivalent in pytest would probably be monkeypatch?
def fake_func(*args, **kwargs):
return "hello world"
def test_something(monkeypatch):
monkeypatch.setattr(my_namespace.my_module, "my_function", fake_func)
yeah, but this doesn't work well
the patch in unittest lets you do things like assert how many times the function was called and with which arguments
!pypi pytest-mock
yeah, I came across that, didn't totally grok the example I think
using the fixture is equivalent to using the context manager
I guess what I want is the stub
it's just a shame that it's so verbose
well, maybe "so" is an exaggeration
but more verbose
from pytest_mock import MockerFixture
def test_foo(mocker: MockerFixture) -> None
mock_foo = mocker.patch("a.b.foo")
...
it has the same interface as unittest.mock.patch so you can define any test double that you want
can I use it as a decorator?
yeah that should work, but then of course you don't get access to the thing you patch in
or you could use it the context manager
pytest-mock doesn't add new functionality, it just reduces some boilerplate
oh, yeah
func_name is the patched thing, so you can assert how many times it's been called and such
that should work
..i forgot it worked that way
the catch is that if you are using this, and using pytest fixtures you have to be extremely careful about parameter order
there might be issues related to interaction with other fixtures
not necessarily wildly intuitive
right, that too
I've been fixing up more stuff, I found a test suite that had a warning because a coworker implemented file cleanup using __del__ π
yikes
probably what happened was, the object was instantiated 3 times for 3 tests, the file was created 3 times in the same place
then all the dels run at the end
and threw
heh
at any rate, I used my first "yield fixture"
quite nice
I wonder if there's a script that transforms all the different flavors of self.assertEqual etc into simple asserts
that's the most time consuming and least interesting part of this
@hexed meadow I used it on coverage.py: https://github.com/nedbat/coveragepy/commit/843de4ea
@hexed meadow it wasn't perfect: https://github.com/nedbat/coveragepy/commit/6e93714d
yes, if you look at the line counts of those two commits, it was a clear win.
@proud belfry
Guys, I have strange behaviour I never noticed before..
I have IneEnum class, lets say:
class Numbers(IntEnum):
one = 1
two= 2
When I do:
print(f"{Numbers(1)}")
I get: Numbers.one.
BUT once I do it in unittest:
import unittest
class TestNumbers(unittest.TestCase):
def test_one(self):
print(f"{Numbers(1)}")
self.assertTrue(isinstance(Numbers(1), Numbers))
I get: 1
What the hell is going on here? I have not noticed it before. (In pytest).
Python version: 3.10
print(f"Numbers(1)") is a string, not interpolation
print(f"{Numbers(1)")....has curvy bracket in the beginning.. incorrect syntax that is not even executable.
are you writing with chatGPT or smth π
On mobile. I obviously cannot post here my real code. This was just a example and I did mistake writting it.
Anyway, if it was what you say, it would return "Numbers(1)" not "1".
well, u don't even post your real code here. What can we speak about then
Post exactly code u have
I think there might be some subtle difference that you missed. That's why we insist on copy pasted code.
why is it obvious that you can't post your real code?
yeah... tiny factors like " quotation marks, or ' single quotation marks
or { brackets
everything matters. we can't really help you if u don't write your real code.
We are just debugging wrong issues then
print(f'{Numbers(1)!r}') produces very different results and that's pretty subtle
Cybersecurity reasons, its for my job
there are secrets in your test code?
I have edited my original post. Now its executable.
from enum import IntEnum
class Numbers(IntEnum):
one = 1
two = 2
print(f"{Numbers(1)} first")
import unittest
class TestNumbers(unittest.TestCase):
def test_one(self):
print(f"{Numbers(1)} second")
self.assertTrue(isinstance(Numbers(1), Numbers))
if __name__ == '__main__':
unittest.main()
prints:
1 first
1 second
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
So yea, I still think it's something else going on that you missed and we can't see it because you didn't copy paste the original real code.
@inner field I also can't reproduce what you are seeing: https://gist.github.com/nedbat/16760433a60efd03b09738493fb7baa5
same. We could question how you execute code to receive different result.
Like, if to drop Numbers(1) to shell directly, u will get its repr way of view
Ah, that's a nice theory
when using tox and pytest, i get a import error
ImportError while importing test module 'lib/tests/..../test_batched.py'.
Hint: make sure your test modules/packages have valid Python names.
E ModuleNotFoundError: No module named 'lib/module'
Running pytest on the current venv, works fine
python3 -c "from lib import module.py" also works
i added to tox.ini
pip3 install -e .
on commands and it worked but now i face with a smaller but new issue
nvm\
found the issue
i had a .coverage file and deleted it
flake8: OK β in 24.92 seconds
py311: OK β in 33.05 seconds
py310: OK β in 33.73 seconds
py312: OK β in 34.78 seconds
py39: OK β in 36.08 seconds
py312: OK (34.78=setup[25.73]+cmd[7.31,1.74] seconds)
py311: OK (33.05=setup[22.86]+cmd[8.05,2.13] seconds)
py310: OK (33.73=setup[24.51]+cmd[7.25,1.97] seconds)
py39: OK (36.08=setup[27.07]+cmd[7.32,1.70] seconds)
flake8: OK (24.92=setup[22.65]+cmd[2.27] seconds)
mypy: OK (46.46=setup[26.26]+cmd[20.20] seconds)
congratulations :) (46.60 seconds)
very good!
:incoming_envelope: :ok_hand: applied timeout to @proper wind until <t:1710733929:f> (10 minutes) (reason: duplicates spam - sent 4 duplicate messages).
The <@&831776746206265384> have been alerted for review.
oh sorry did just a typo on the explaination
but yeh exactly like that i did
no worries tho
the problem is solved
needed to add a pip3 install -e . before executing pytest
pytest shouldn't be a production dependency, only a dev dependency
tox should be a dev dependency too
i installed the package im working on
I guess he uses a src directory structure?
yup
what's that got to do with dev dependencies?
Nothing? only you have mentioned it
yeh
yeah i noticed that pip install -e . is installing tox or pytest (which is why your commands afterwards work)
Which means its added as a prod dependency
hmm.. no I think you're still confused
????
Obtaining file:Users/.../Lib%20Lib/lib
Installing build dependencies ... done
Checking if build backend supports build_editable ... done
Getting requirements to build editable ... done
Preparing editable metadata (pyproject.toml) ... done
Building wheels for collected packages: lib
Building editable for lib (pyproject.toml) ... done
Created wheel for lib: filename=lib-0.0.0-0.editable-py3-none-any.whl size=1487 sha256=####
Successfully built lib
Installing collected packages: lib
Attempting uninstall: lib
Found existing installation: lib 0.0.0
Uninstalling lib-0.0.0:
Successfully uninstalled lib-0.0.0
Successfully installed lib-0.0.0
@gentle swan can you share your pyproject.toml or setup.cfg?
Whatever you are using to store package metadata
i got both
again. this is not what pip install -e . does
it installs only prod deps afaik
pls do share
[build-system]
requires = ["setuptools>=69.1.1", "wheel"]
build-backend = "setuptools.build_meta"
[tool.pytest.ini_options]
addopts = "--cov=lib"
testpaths = [
"tests",
]
[tool.mypy]
mypy_path = "src"
check_untyped_defs = true
disallow_any_generics = true
ignore_missing_imports = true
no_implicit_optional = true
show_error_codes = true
strict_equality = true
warn_redundant_casts = true
warn_return_any = true
warn_unreachable = true
warn_unused_configs = true
no_implicit_reexport = true
and cfg
[metadata]
name = lib
description = ...
author = McBrincie212
license = "GNU General Public License v3.0"
license_file = LICENSE
platforms = unix, linux, osx, cygwin, win32
classifiers =
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.7
[options]
packages = lib
# install_requires = numpy>=1.26.4
python_requires = >= 3.7
package_dir = =src
zip_safe = no
[options.extras_require]
testing =
pytest>=8.1.1
pytest-cov>=4.1.0
mypy>=1.9.0
flake8>=7.0.0
tox>=4.14.1
[options.package_data]
lib = py.typed
[flake8]
max-line-length = 200
yeh, no worries it happens
yea :P
sorry for causing the confusion
no probs
i just got a bit too habituated with npm recently
I am also confused about this stuff constantly :P
just know for pip3 install -e .
-e: Editable Mode
. refers to the folder(in my case src)
hm?
hopefully uv will fix all this chaos
so you have one less file π
What's that
yeh
the tutorial in question
https://www.youtube.com/watch?v=DhUpxWjOhME
Take your Python project to the next level of professionalism.
Automated testing in Python is an important way to take your Python project to the next level of dependability and professionalism. There are a lot of steps, but it's not too difficult to setup your tests to run automatically across multiple different operating systems and versions ...
new package manager from the same people who does ruff
i m saying you can put all your setup.cfg contents inside the pyproject.toml itself so you have one less file @gentle swan
currently its not wrong, but pyproject.toml was created with the intention of being able to store project metadata
can it be vice versa?
like pyproject.toml to setup.cfg
possible but idk, pyproject is the modern alternative
it depends on whether the tools you use still support setup.cfg configuration
do we really need one more package manager?
very true
we already got pypi
so what gives?
We need a good one that people rally behind.
setuptools is good enough tbh
no, that's a package repository. different thing
it can handle the most complex packaging requirements already
uv is WAY faster
oh, sorry
So much faster that it could run dependency check before every run command and you wouldn't notice. it would be great.
I tried to build some tooling that did the right thing for python once but I couldn't get it to work because pip was too slow. With uv there is hope again
lol
both afaik. And venv
So its more like cargo
Ruff has been a huge success so far, so I think they can get people behind this.
very much. That's the goal. cargo for python.
sounds promising
oh yea, and ruff now has a code formatter that is like black but way way faster
These guys are doing great work
yea i had heard about it when it was in works
damn
Is it 100% black compatible?
not quite, but very close
idk I use mamba and it's pretty amazing
I didn't realize ruff was doing packages as well
oh sorry, that's a separate thing
at any rate, I think conda/mamba are sort of in a separate league from anything else, because they also handle native dependencies. it's a huge gamechanger.
it's a shame that often people outside the quantitative python community seem less aware of them
less aware of them because people generally can make do with the defaults
idk, pip is a bit better now but it was like completely broken until relatively recently
didn't properly solve dependencies
from firsthand experience I think a lot of people just used pip because it was there and then suffered for it
rather than it actually being "good enough" in a meaningful way
pip is on homeopathy, it gets fixed very slow
Where did you guys read Test Driven Development by Example by Kent Beck?
Kindle version is a bit cursed...
Hello, i have a python program which writes something to a file, If i want to create a test for this, whats the best method to do so? When i searched for this i came across unittest.mock. Is that a good method or is there something else which is better than mock
Create temp folder and write to file. No need to mock such stuff
U make only worse with testing less
The best way is to not write to disk, but write to a file handle you pass in. so you can pass in a StringIO or BytesIO.
Thanks for the advice, i will look into both
anybody used pynguin?
question about pytest; when it parallelizes, will it run tests that use the same fixture on different cores?
I'm wondering about a fixture that creates and then deletes a file at a fixed location, and whether that would thus be unsafe to parallelize
and instead I should use some kind of unique temporary dir instead
use unique temporary dir instead
yeah, I'll probably do that, but I just wanted to understand what rules pytest uses for parallelizing, especially different scopes of fixtures
You could give a detailed reading here https://pytest-xdist.readthedocs.io/en/stable/how-it-works.html
though it does not really seem to discuss fixtures
but it seems to me like it just doesn't care about fixtures. it just divies up the tests however it wants and then does the fixtures however
Question: Why does each worker do its own collection, as opposed to having the controller collect once and distribute from that collection to the workers?
If collection was performed by controller then it would have to serialize collected items to send them through the wire, as workers live in another process. The problem is that test items are not easily (impossible?) to serialize, as they contain references to the test functions, fixture managers, config objects, etc. Even if one manages to serialize it, it seems it would be very hard to get it right and easy to break by any small change in pytest.
parallelism through Processes does not share memory
well, yes, if you ctrl-f fixture, that is what you find π
that doesn't really answer my question in the sense that it could decide to keep tests sharing a fixture that's higher scope than function level, on the same worker
for function level fixtures it doesn't technically either, but I guess it's pretty safe to assume that function level fixtures could end up running on different workers unless I see evidence otherwise
since in principle it doesn't even save work
by default fixtures are executed in scope, for each test to repeat again
u need applying session scope to reuse them
but since xdist are Processes, they don't share memory
so they rerun everything including migrations on test session start
uhm, again, this doesn't answer my question, but it's okay, if you don't know the answer you don't need to try to answer it π
anyhow seems like this answers it at least for session-scoped, not sure about smaller scopes
You can get the number of the xdist worker for creating the directory.
is there an advantage to that over just using a temporary dir?
it might be easier to understand if something is going wrong.
fair enough; it's all getting deleted afterwards anyway
I'll keep in mind for the future though that it's possible to get that number, thank you
about your actual question: I think xdist (the parallelizing part) is separate enough from pytest that it doesn't know about fixture scopes, etc.
interesting
that's definitely something you really need to be aware of
like, you could easily imagine a session level fixture that really does not work well to create multiple times
is tempted to test this empirically
@hexed meadow a quick empirical test shows that each xdist worker runs the session-scoped fixture.
nice!
thanks for that
A = ["1", "2"]
A = ' '.join(A)
A1 = A.split(' ')[0]
A2 = A.split(' ')[1]
A3 = '1'
A5 = '2'
A4 = A1 if A3 == A1 else None
A6 = A2 if A5 == A2 else None
A4 = A4 if A4 is not None else ''
A6 = A6 if A6 is not None else ''
A7 = A4 + A6
print(A7)
Did smthin wrong ?
Yea, you did something wrong. You didn't explain at all what you are talking about :P
Seems right to me output should be β12β
But seems like a bunch of unnecessary stuff
Is it a homework assignment?
I have a very weird problem. I am implementing a Python tool that generates some Python code as a string. When writing the tests, I noticed something very odd. For testing, I was generating multiple strings and asserting these can be interpreted by the Python interpreter. This will help me to detect if I am missing some imports or lines. However, I noticed that the same code leads to two different scenarios:
- If I save the file and execute it via "python script.py" using
subprocess(or even manually in the terminal), everything is fine - If I do
exec(string)I get aNameErrorerror saying that some variables do not exist. If I inspect the string I can clearly see these are defined. I can even execute the same script usingpython script.py. It just does not make sense
For more context, the file is just a collection of type aliases (some of them are nested). There are not any global variable and all imports are within the typing module
Any clue about what's going on?
Edit: I found something about compiling the code before if your program is a multi-line one. I never heard about it, gonna test it tomorrow
hmm, i don't understand that. this works fine: exec("a = 1\nprint(a)\n")
It is just failing under the testing environment. If I save the string and do exec(string) in a separate file, it works as well. It is really strange
Try repr of the string to check it carefully.
Anyone used pytest with pytest-timeout plugin? Except for the detailed README, do you know if there are any gotchas to be aware of?
I am creating a flask application with several view functions. Should I test the rendered HTML template for the responses of view functions or, just their status codes (as I have seenn many projects do)? What is considered good? Thanks...
Test the rendered output
Probably check that parts of the rendered output that is critical shows up. Jinja will silently render empty strings when you misspell a variable so just checking status code isn't great.
Ah. And checking the full output will suck :)
Okay...can I use flask's render_template function in my tests somehow?
Bs4 to check parts imo.
that seems good...
Smoke tests (just checking for status code) are better than nothing
Just rely on your views to generate HTML don't worry about how they do it
Yes but that won't check the logic implemented in Jinja templates like displaying flashed messages or showing the logged in username etc...
You can check the flashed message shows up in the HTML your tests don't need to call render_template
Got it. I would check for status code and only some important parts of rendered HTML which are crucial using something like bs4.
Thanks
You might want snapshot tests that render the HTML to a PNG and you commit what it's supposed to look like
Great, but I think that would be too complex for a beginner like me who is creating his first flask project from scratch.π . But I would definitely consider it.
Thanks once again
It can also be the wrong thing if the design should change a lot.
Hmm...
Also, I think it is also dependent on the device for which the snapshot is generated for that HTML (I am assuming that @hexed cloak was talking about the HTML rendered output page in browser.)π€
If it's as easy to update a test as:
"this snapshot has changed, this is how it looked before and this is how it looks now. Accept the new look? [YES/NO]"
Then frequent changes I ought not be a problem
Honestly this channel should be called just #testing
To cover smoke and snapshot and functional and unit tests
Everything begins from unit testing π
it is a good name enough
if we stretch testing too much, it will be also Manual QA, e2e testing too much and etc
best to begin with unit testing π and then offtop everything else in same channel
And test coverage, and random testing, and hypothesis
Which we cover in here all the time. So yea. Good point π
Also stuff we cover in here commonly heh
Yes, I agree with you
<@&267628507062992896> can we rename this channel to testing? It's what it's actually about.
The channel is about automated testing in general, yes. If we change it to just "testing", people might not realize that we have a channel for pytest et al. Whereas I don't think people see "unit testing" and think "oh, I don't think I can ask about integration testing there"
We do? I scrolled through the list of channels three times and I don't see it.
Sorry, which channel did you not find?
When I said "a channel for pytest et al", I was referring to this channel, the one we are currently in.
Oh. I totally misread what you wrote above. I thought you meant you wanted to distinguish this channels name from #pytest or something.
"testing" by itself might also suggest spamming or testing of discord features
Seems a but far fetched to me. But maybe yea.
Can someone advice the best approach when you need to unit test functions that act on JSON data?
I have functions that process JSON data with a particular structure. Would I hard code what this JSON structure looks like into the unit test?
Maybe. Or select just parts that are critical. Or maybe validate with pydantic and you don't need much in terms of test code, you can just run the function and check that it doesn't crash.
What does the function actually do? The basic I approach I follow is testing function behaviour. So given an input, assert the function outputs what I expect.
If the function accepts JSON as input then that's the function's public interface. I don't see an issue with the tests being aware of the input's JSON structure - they have to be in order to use the function. I wouldn't consider that "hard coding"
Thank you!
Thanks
Would you say it's always necessary to unit test every aspect of a project? Basically I am unit testing the core logic of my program but I am not bothering with testing the command-line interface aspect of it, as I'm using argparse and I know that unit testing the various errors can be difficult as it prints the error messages rather than raise exceptions. I'm also fairly confident in the argparse module catching any of the obvious errors.
That depends on how well u managed to draw code isolation between argparse and invoked code layers
And we can add additionally that it depends on how much of argparse code u have
If isolation is zero, and amount of argparse is high => definitely should be tested
If isolated well or amount of argparse is small.. then almost no point to test
Essentially there is no point to unit test already tested framework
But if your code is too much intermingled, then testing framework (argparse) is the only way to test your custom code too
Unless u architectured better your own code
I would recommend (since u have doubts to test it or not to test it) as minimal setup to unit test at least few argparsing related code invoking yours just for the sake of keeping in mind that u can write your code with ability to test it with integration to argparse if necessary in a future. And if u see no point to test today, then don't test it. Just keep writing with keeping in mind ability that u will be able to test it if it if u ever change your mind
Keeping this aspect in mind will keep code architecture within fixing easy reach to do it if desired. That allows making delayed decision about it.
Okay thank you!!
I appreciate the extensive response.
:incoming_envelope: :ok_hand: applied timeout to @flat fiber until <t:1711640519:f> (10 minutes) (reason: duplicates spam - sent 4 duplicate messages).
The <@&831776746206265384> have been alerted for review.
hello guys
look at my new i think finished project
the chat budy
buddy
from websockets.sync.client import connect
import json
print("ChatMate")
def main():
addr = input("address:")
name = input("Nick:")
with connect("ws://"+addr) as ws:
ws.send("login "+name)
while True:
data = ws.recv()
print(data)
if data == "menu":
print("1.Show Rooms")
print("2.Disconnect")
c = input(">")
if c == "2":
break
elif c == "1":
ws.send("list")
elif data == "list":
data1 = ws.recv()
if data1 == "empty":
print("no rooms")
else:
ls = json.loads(data1)
for x in ls:
print("room "+x["id"])
while True:
print("1.join Room")
print("2.Add Room")
print("3.back")
c = input(">")
if c == "3":
ws.send("list")
break
elif c == "2":
name = input("Room Name:")
ws.send("mkrm "+name)
elif c == "1":
name = input("Room Name:")
ws.send("join "+name)
data = ws.recv()
if data == "succ":
while True:
ws.send("getchat")
data = ws.recv()
chat = json.loads(data)
print(data)
for mess in chat:
print(mess)
msg = input(">")
if msg == "-e":
break
ws.send("msg "+msg)
main()
its just a client that sends a message to a server that then gets put into a "room" (a list of messages) that people can read into
at the time im using ngrok for tunnelling
but if anyone has forwarded ports
i mean you could set up the server on your router directly
Tempted to add this to my collection of code examples in dire need of refactoring.
i mean... don't be discouraged... i wrote similar stuff in the beginning. π
But still could be nice to improve later.
i am slightly confused though what to recommend π€ the needed material i learned during first year of uni and not knowing good book to recommend for self studies
you need to learn... concepts of Functions, Classes... ah... i know
https://www.oreilly.com/library/view/head-first-python/9781492051282/ This one should work for you to learn next material for this code improvements
Tempted to add this to my collection of definitely discouraging ways to talk to people.
@maiden pawn you are prolific in this channel, I hope the wink means something positive.
because it's an unfortunate dynamic that some people who know things also think "tough love" is a good way to teach people stuff.
I don't see the big problem with the code. There are no variable names that lie and it's straightforward.
The biggest problem seems like it is that it should handle exit in a nicer way, like putting it as an option in the menu and/or handling ctrl+c.
If you add a class to that code it will help worse.
I'm not keen on the levels of idention, I'd be tempted to split each of the while loops into function calls with nice names
Hello friends, I came across this useful article that helped me with a problem I couldn't overcome regarding Flutter. I think it may be useful for friends who encounter the same problem.
Dr. on Overcoming Challenges in Flutter Development Test Automation with Katalon Studio. Mehmet Fatih HarmancΔ±'s article: https://virgosol.com/tr/blog1/detay/flutter-test-otomasyonunda-karsilasilan-zorluklari-katalon-studio-le-asin
Katalon Studio, Flutter ile geliΕtirilen mobil uygulamalar iΓ§in test otomasyonu yaparken bazΔ± zorluklarΔ± Γ§ΓΆzmenize yardΔ±mcΔ± olabilir. Hemen TΔ±kla!
How to mock a class initialised outside the function
db=DB()
def func:
db.call()```
How to write unitest for this iam not able to mock DB object initialised outside. Please help me with this
You should show an actual working example of the problem. You showed invalid code.
Also, do you get an error or something?
Hi, does anyone have experience with unit testing in Python using unittest? I'm somewhat new to unit testing and I'm having trouble patching the gather method of asyncio π₯Ή
that sounds like a tricky thing to patch. Can you link us to some code to look at?
i recently patched logging library method for silly reasons of injecting my tracing data into every logging record for any python app without touching their code ^_^
import functools
import logging
from typing import Any, Callable
from . import settings
from .data import get_trace_data
def _w_makeRecord(func: Callable) -> Callable:
"""
Inspired by ddtrace.contrib.logging.patch._w_makeRecord
"""
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
record = func(*args, **kwargs)
data = get_trace_data()
setattr(record, settings.RECORD_ATTR_SERVICE_NAME, data.service_name)
setattr(record, settings.RECORD_ATTR_TRACE_ID, data.trace_id)
setattr(record, settings.RECORD_ATTR_SPAN_ID, data.span_id)
return record
return wrapper
def patch() -> None:
"""
Patch ``logging`` module in the Python Standard Library for injection of
tracer information by wrapping the base factory method ``Logger.makeRecord``
Inspired by ddtrace.contrib.logging.patch.patch
"""
if getattr(logging, "_apm_patch", False):
return
logging._apm_patch = True # type: ignore
logging.Logger.makeRecord = _w_makeRecord(logging.Logger.makeRecord) # type: ignore[method-assign]
hehe, used decorator "manually" for that
How is that relevant?
this is an example to patch std library method of course.
asyncio.gather can be patched in same way for tests if necessary
that's if having trouble using standard unittest.mock.patch thing
yea, and you didn't bother to find out the real problem first. This is my issue.
this snippet is part of a fastapi route that im testing. Basically, getToken returns a jwt token (βtoken-string-blablablaβ) and signXML returns an signed xml file. At my noob understanding, βreturn_valueβ specifies the value I expect from the original function (for example, if I have a function that returns 'hello', my return value for the mock would be something like mock_hello.return_value='hello') and what I understand the gather method returns a tuple with the results of the functions passed as parameters and thatβs what im trying to simulate here.
Now, my problem is that seeing my log from docker I think im patching it wrong. In the same route I have two gather methods. But for now, I'm only interested in patching the first gather method. When I look at the Docker log, I see some error logs.
The first error log ('str' object has no attribute 'content') leads me to believe that my patch of gather is also used in the second gather method (? due to the line json.loads(response_api_url.content.etc).
But yeah, im pretty lost so any advice is welcome.
idk if its clear as this is my first time using discord but the first image is the gather method being used, second png is my test, third is another gather method used in the same route and lastpng is docker logs
Wouldn't this be a lot easier to do by just installing your own log handler?
I would offer that patching the gather function should be a desperate last resort that you should almost never consider for testing your application
probably not. I did it to ensure this logging was caught correctly into Django Logging
i am not sure if u can easily install log handler into django logging to make the same...
...probably u can. i don't know how π
I also thought the same thing, but I'm concerned about how to perform the test for requests to external APIs (third image) and from what I understand, mocks are recommended when making requests to external APIs. That's why I've tried to patch the gather method since it's the one handling those requests π¦
gather isn't "handling" the request though. client.post is what's actually performing the request
but what are you actually trying to test here?
I want to test that my route handles the responses from those requests properly. Ohhh so should I patch the AsyncClient object (client.post) instead?
better yet, write your code to accept a client as a parameter, then you don't even need to patch it, you can just pass in a dummy client thing
def submit_request(client: httpx.Client, ...) -> Something:
resp1, resp2 = await asyncio.gather(client.post(...), client.post(...))
...
or even better still, write one function to submit the request and another function to handle the responses from it, then you can test the 2nd function without any mocking or patching at all
def perform_requests(client: httpx.Client, ...) -> tuple[httpx.Response, httpx.Response]:
return tuple(await asyncio.gather(client.post(...), client.post(...)))
def process_response_data(resp1: httpx.Response, resp2: httpx.Response) -> Something:
...
def run_requests(client: httpx.Client, ...) -> Something:
resp1, resp2 = perform_requests(client, ...)
return process_response_data(resp1, resp2)
so now you can test process_response_data as a "unit", and testing run_requests is now more of an integration test, just making sure you didn't screw up the handoff between units
likewise perform_requests becomes almost trivial, there's almost nothing to test anymore
You're right, it's better to separate the requests process into individual functions if I want to test. I'll consider both alternatives you gave me, thank you sooo much!
But I still have a doubt, if you dont mind, do you think it's a good idea to patch the gather method, or is it a tedious process that's not worth it under any scenario? I'm not referring only to my example using gather, but to any other process where it's being used
tedious process that's not worth it under any scenario
yes
it's not necessarily tedious, as much as it's highly "invasive" and imprecise
it also tightly couples your test to the implementation of your application
what if you refactor to use asynio.TaskGroup?
haven't heard about that yet but i'll look it up, thanks:D
PyCharm is just an editor, so I don't see how that would make a difference. And implement what? That's incomplete code. And is this test related?
Yes, this is a test related
Who can i check my own test case like class solution
Maybe you want to read up on pytest?
Ok let me try
Guys quick question, can you write doctest for ABC classes just to show how to use the class and its methods, or do you have to do it just for the subclasses?
You can create a concrete class defined in the doctest.
What's a concrete class?
Ohh I get what a concrete class is now. How would I go about creating a concrete class for a ABC in doctest?
By the way, if you write doctests for a ABC, when you subclass that ABC and only provide the definitions for the abstract methods without changing the parameter types, or return types, would the doctest be inherited, or would you need to create a duplicate docstring with a duplicate doctest included each time?
Just write a class in the code
You can write any code you want.
Ok, so if I have the ABC, and I also have the concrete classes, where should I put the doctest?
The main reason I am trying to put it in the ABC one is to reduce duplicate docstrings.
Also, I think I may have confused you a bit, I have testers to ensure the codes run as expected, so I don't need doctest to run, I simply am considering it for documenting usage examples.
I hope that makes sense.
Sure.
So, could I have doctest format just to show usages without getting errors since I am not running them?
i.e.,
@abstractmethod
def get_counts(self,
num_shots: int,
backend: Backend | None=None) -> dict:
""" Get the counts of the circuit.
Parameters
----------
`num_shots` (int):
The number of shots to run.
`backend` (Any | Backend):
The backend to run the circuit on.
Returns
-------
`counts` (dict): The counts of the circuit.
Usage
-----
>>> circuit.get_counts(num_shots=1024)
>>> circuit.get_counts(num_shots=1024, backend=backend)
"""
pass
I won't run doctest here for instance, it's just to show how to use the method.
Would this be fine?
if you aren't running them, then you can do whatever you want. I don't think those examples you showed are very good though. You should show the return value if you keep them.
i'm not sure this function needs an example.
How should I know when to include usage example?
or, it does, because it returns a dict. you don't explain the structure of the dict.
Is there a rule of thumb perhaps?
So, I just wanted to show how to use the method, and didn't think it was important to bother with showing the return values when the return is not None.
Should I include Return for the examples as well?
there's nothing here that explains the return value.
I don't understand what dictionary is being returned.
I see, so in this case I should include an example of what is returned?
i don't want to say "in this case". I want you to see that you have a lot of syntax in this docstring, but not much actual information.
put yourself in the user's shoes: will they understand what they need to know from what you have written? Have you fully described the function?
you should say "returns None" or something to that effect.
No no I get the annotation part, I meant the Usage documentation.
Like just
>>> obj.method()
?
what you have now is how it looks if the function returns None
Yeah
will your users know what "counts of a circuit" means?
Ok, I think I understand now, thank you so much sir!
Yes, but I would like to take your advice and make it clearer for the general programmer/user.
ok
That seems silly. Doctest will check that the docs are correct. Why throw that away?
Ohh because I already have testers.
I just wanted to documet usage examples, and liked the doctest style.
The >>> thing.
I've spent TONS of time getting my docs in one of my projects to be run as part of the test suite. I've cought many many errors that way.
True, but for my case I have both mypy and pytest checking everything, I don't know if there are any other errors that would be missed.
I mean, doctest just runs the method, which I have testers for that I run using pytest.
what? It runs the code in the example. That's the entire point of it.
Exactly, which I cover using my testers.
>>> 1 + 1
2
is the same as pytest
assert 1 + 1 == 2
what? You run pytest-doctest too?
You are not making sense here
No, just pytest.
I have a folder containing my tester .py files, which I run to check my code using pytest.
Then how are you testing the example code in the docstring?
I don't test that example exactly, I do check examples in my tester files though.
THIS IS MY POINT. Dude. doctest WILL CHECK THE EXAMPLE
Having correct examples is GOOD. Having incorrect examples is BAD.
You can make your point without being condescending
doctest will try to run the code given. For those, you want to only show good code
I'm not being condescending. I'm being frustrated.
If you're frustrated, then take a step back. Getting snippy with other users is not the behavior we want here
Sound good?
Hey! when using @pytest.mark.filterwarnings, how would I pass my own warning to ignore?
I tried doing @pytest.mark.filterwarnings('ignore::MyWarning') but that did not work
If it's your own warning you'd usually use with pytest.warns(MyWarning)
You need to pass the absolute module path to your warning
I have a file
from db import DB
db=DB()
def check():
#code```
Here DB intialisation tries to connect to the database. I want to mock DB(). I tried @patch('check.DB,''), but iam not able to mock it. Share your valuable insights.
Ah that worked, thanks
I wish there was a prettier way though
How does your code use db?
Can you paste your actual code and test?
Has anybody used any tools to automatically generate unit tests for Python? I just tried ChatGPT and PyCharm/IntelliJ, which each produced their own version of 'kinda helpful, but not really'.
I see somebody mentioned pytest-doctest above, I'll take a look at that.
Copilot is pretty good at creating simple code if you name functions/variables really well. Any small error in naming results in bad code though in my experience. Which makes sense as you ask it the wron thing.
But it should be used to reduce typing, not to actually write any code.
:incoming_envelope: :ok_hand: applied timeout to @barren onyx until <t:1712340695:f> (10 minutes) (reason: duplicates spam - sent 4 duplicate messages).
The <@&831776746206265384> have been alerted for review.
heloo guys
Traceback (most recent call last): File "threading.py", line 1052, in _bootstrap_inner File "threading.py", line 989, in run File "serverweb-2.py", line 2615, in helo PermissionError: [Errno 13] Permission denied: 'emulator-5566.txt'
Why
You're not closing your files correctly there. You need to use with open(...)
Not sure why you're getting permission denied though
I have problems running pytest with coverage (for Codecov) in GitHub actions. It reports a segmentation fault from running pytest, but after the tests have completed and the coverage file is generated. The test suite works fine on my computer. A possible suspicion is mem usage, but I'm stuck on how to proceed. I couldn't find anything similar on Google. Is there anyone here that might help shed some light on how I might proceed?
try to simplify the situation. Are you using pytest-cov and codecov? Perhaps removing one or the other will change the outcome. Do you have a detailed error report?
I'm using pytest-cov and codecov (because I want to feed the data into Codecov). The error is here https://github.com/Laerdal/python-objdictgen/actions/runs/8590177138/job/23537359537, however the error message on the crash is rather short ....063d58.sh: line 1: 1833 Segmentation fault (core dumped) pytest --cov=objdictgen --cov-report=xml -p no:logging
yup, that's difficult. you could also try bisecting your test suite to find the cause.
Yes, attempting to disable cov now
Bisecting on actual GitHub action runs are slow π -- I point my suspicion at wxPython. Whenever the loading of wx is not a part of any of the tests it doesn't crash.
Actually DB initialises the database connection
DB seems good for that
hello folks. im trying to write unit tests for a compiler but i have to format the tokens onto 1 single line and unfortunately i wrote the tokens mimicking the main file structures. i dont know my unix command well but i want to take every tokens and space them by one whitespace all of them onto 1 single continous running line.
why do you need to format the tokens in one line?
its actually a java file but i want a command that can take away newlines and >1 whitespaces
cuz its for a lab im researching for in school and the way the author wrote it (using junit) needs the tokens to be one spaced, all on 1 line
so like right now its like this:
ID(has_left) BECOMES FALSE SEMICOLON
ID(has_right) BECOMES FALSE SEMICOLON
RETURN TRUE SEMICOLON
RBRACE
PUBLIC BOOLEAN ID(SetRight) LPAREN ID(Tree) ID(rn) RPAREN LBRACE
ID(right) BECOMES ID(rn) SEMICOLON
RETURN TRUE SEMICOLON
RBRACE```
basically all of that needs to be in 1 line, one-spaced in Line 1
hello, I need a little help with something, is anyone free to help me out here or in dms?
Ask the question
I have a code that is giving the output in jupyter notebook but the dash app isn't running for some reason
can I post the code here?
This doesn't sound like a unit test thing. Maybe use the help system instead. And yes post the code there. Always post code when asking for help.
pytest question: I have a resource that I want to be shared between all the tests in a given module. I don't want that resource to be re-created for every test (ie, not a regular fixture), as it takes a long time to create and isn't stateful. Is there a way to specify module-level setup and teardown?
I don't want that resource to be re-created for every test
you need to usepytestfixture with scope session
@pytest.fixture(scope="session")
def smtp_connection():
with smtp_connection_base() as result:
yield result
should be like that
Optionally scope="module" can work too since u named module scope
@pytest.fixture(scope="module")
def smtp_connection():
with smtp_connection_base() as result:
yield result
@maiden pawn thanks!!!
at your service
Hi! I am using unittest and MagicMock. I have function process_website(...) and I want it to return [{'ad_data': 'example'}]. But it returns <MagicMock name='process_website()' id='2397875500816'>. What am I doing wrong? Here is my setup:
@patch('src.sql_server_database.SQLServerDatabase')
@patch('src.scrapers.ScraperFactory')
@patch('app.get_external_model_id')
@patch('app.process_website')
def test_get_ads(self, mock_database, mock_scraper_factory, mock_get_external_model_id, mock_process_website):
# Set up mocks
mock_scraper = MagicMock()
mock_scraper_factory.get_scraper.return_value = mock_scraper
mock_database_instance = MagicMock()
mock_database.return_value = mock_database_instance
mock_get_external_model_id.return_value = 'external_model_id'
# Define sample data
request_args = {'MakeId': 2, 'ColorId': 3} # Example request arguments
# Mock process_website to return sample data
mock_process_website.return_value = [{'ad_data': 'example'}]
# Call the function to test
result = app.get_ads(request_args)
And in get_ads() function I have:
print('processed website: ', process_website('')) which prints:
<MagicMock name='process_website()' id='2397875500816'>
Try calling from the app namespace (app.process_website) so that you actually call it
Could you explain a little bit more? I didn't quite get what do you mean
Hi
Polars
I haven't posted this in a long while so I thought I would drop a reminder because its so easy for things to get forgotten:
- I wrote a library to improve mocking, called MegaMock. It fixes issues with patch, resolving the problem where how you import things matter, and makes it easier to create good mocks. I use it frequently and you can get it here: https://github.com/JamesHutchison/megamock
- I also wrote a library to hot reload pytest. I just (today) updated it to better workaround a line number issue from the dependency hot reloading library jurigged (https://github.com/breuleux/jurigged) you can get the hot reloader here: https://github.com/JamesHutchison/pytest-hot-reloading I also use this very frequently
If you want to quickly jump in and test them out, I have written a "batteries included" web stack called the "Heavy Stack" and it comes with a dev container config which you can use with GitHub Codespaces. Just create a codespace and jump to the example test. You can find that here: https://github.com/heavy-resume/heavy-stack/ The pytest hot reloader typically uses a Daemon launch config and that's already set up for you.
can you say more about how it resolves the problem of how you import things matters?
The plugin installs an import hook to track imports. When you do a megapatch it hunts down all the locations where there's a new reference created and patches them.
got it, thanks
How do you make sure that hook is installed before any modules are imported?
Pytest has plugin hooks that run at different times. There's one that runs really early.
It might be good to include this information somewhere in the documentation, I didn't see it. "How it works" is often important for skeptical busy programmers who might benefit from your tool but might be afraid of using a new tool without understanding what it actually does
I thought I had that but now I'm wondering if I ripped it out because it was too much information. I'll add a "how does it work?" section that is brief like I was here. Thanks!
thanks! in the future you can always put this in a separate document
github wiki is a good option for quick and easy
can link to it as "Documentation" on pypi as well
It's a challenge. I've been working on "heavy resumes" which basically gives you a high level structure with expanding elements and cross references, and an AI agent you can ask questions. While working on it I had the realization that you could really do it for any "flat" document. Would be nice for libraries as well. Markdown is showing its age.
As long as you parse the data coming over the wire and throw errors on incorrect data type that sounds ok.
Your tests will be slower if you do it this way than if it's all in-process though.
You should verify that it is
I just think you will get tests that are 2x-10x slower than they would be if you didn't pass through all the API layers and network stack
still. A function call is going to be quite a bit faster
yea that's true
def validate_image_file(
_ctx: click.Context, _param: click.Option, value: Path
) -> Path:
"""
Check that file named by path has JPEG format.
:param _ctx: Library context.
:param _param: Command-line option parameter.
:param value: Parameter value.
:return: Validated parameter value.
:raise click.BadParameter: File named by path does not have JPEG format.
"""
# constants
jpeg_mimes = ("image/jpeg", "image/jpg") # second is non-standard
jpeg_exts = (".jpeg", ".jpg")
# attempt to determine format
guess = filetype.guess(value)
mime: Optional[str] = (
guess.mime if guess is not None else mimetypes.guess_type(value)[0]
)
# check that format is JPEG
if (
mime is None and value.suffix not in jpeg_exts
) or mime not in jpeg_mimes:
raise click.BadParameter("Input image file format must be JPEG")
# success
return value
if i want to unit test this, do i mock filetype and mimetypes stuff?
or do i just give it paths to files that i've specially crafted?
ok i'm leaning towards the latter, the former feels very brittle (the UT would rely on the specific way that i'm checking the mimetype)
unless someone here thinks that UTs should be brittle like that?
Why not give it legit values that pass and fail the test?
yeah that's what i'm leaning towards
by legit values you mean give it paths to actual files, instead of mocking filetype and mimetypes, right?
Yeah. Using a fixture file or two will remove any of the flakiness that mocking will give you.
At face value, that sounds more like an integration or end-to-end test. Those are usually best in the deployment CI where they can take the time they take. Marking the test and only running it when requested is the way I'd go with it.
In the S3 protocol the client sends the checksum
You could send an infohash from your client to your server, then you'd be able to work out which chunk is wrong if the hash is wrong
hello
Can someone help me?
line 23, in <module>
flappy=Bird(100,int(screen_height/2))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
line 21, in init
self.rect.center={x,y}
^^^^^^^^^^^^^^^^
Where did I go wrong?
I need to make a Flappy Bird type game for my School project
you haven't shown an error to suggest that somepoint is wrong
also this is the unit testing channel 
speaking of which...
I have 2 test functions, each with the code mock_stat.return_value.st_size = <some int value>
at the breakpoint, i run Path().stat().st_size
on the left function, it evaluates to the number, but on the right function, it's an instance of MagicMock
wtf?
Its TypeError: invalid rect assignment
Sory I just started my coding journey in the beginning of this month
if i make the patch("pathlib.Path.stat") the second patch in the right function, that fixes it
so idk wtf
oh ig decorators go outwards
do you have the documentation for the .center attribute?
also you should really ask this in #1035199133436354600 or #python-discussion
Don't crosspost. And this is NOT the right channel.
Why not use tmp_path and real files with real sizes?
because it's a 4GB file
and it doesn't need to have actual data
I would go with functional core, imperative shell, and make the shell super tiny and just don't test that.
Or test that only via the fast path with a small file.
I'm trying to mock the client & pool here to test class A, any tips? https://paste.ee/p/e8xwb
You forgot to say what the problem is
You didn't async with your pool
In my tests I like to use redislite instead of mocking redis
Im currently doing my first steps with python unit testing.
I could need some support on how to correctly mock database and api.
do you have any good documentation or maybe anyone whos available to join a call and show me whats good practice?
Generally you don't mock the database, you use transactions and rollbacks for test isolation instead
so I create a test db and use transactions and rollbacks instead?
and for APIs I use pytest-recording
Yeah, if you're using Django this happens for you
What's your database?
mysql
And you're using sqlalchemy?
No we dont use any sqlalchemy in this project.
even more ideal than what graingert said is to write as much code as possible as pure functions that you can test without mocking and without a db
would you be available for a call to talk about it and maybe explain a thing or two?
No. You ask in here like everyone else.
as it was said use mysql db to run test
testcontainers is interesting flexible solution to raise it in docker containers quickly ^_^
https://testcontainers.com/
and yeah, pytest-django already does it for you
Containers has got to be much slower than what pytest-django does.
I never use a DB when writing and running unit tests, I don't think a unit test should do that. But maybe the responses for your question is about testing in general, and specifically Integration testing?
A unit test, in my opinion, should test the data and logic that is before-and-after the DB/IO calls - testing your code and not the DB integration. For Integration tests, you would probably want a Test-DB running in isolation as suggested. If the code touched by unit tests do some IO/DB things, you would want to mock those out, or patch, or using dependency injection. My preference would be to isolate the DB call in a separate function, and patch that one (i.e. a fake function returning data that is in control of the unit test). I usually do that with pytest and the monkeypatch feature.
I think people use "unit testing" to mean "automated testing" mostly. Especially beginners.
When should I use fixtures and when ordinary helper functions in pytest? Is code below OK?
@pytest.fixture
def get_page_and_expand(selenium):
selenium.get(urls.ELEMENTS_CHECK_BOX)
expand_icon = selenium.find_element(By.CLASS_NAME, "rct-icon-expand-all")
expand_icon.click()
def click_checkbox(selenium, *checbox_names):
for name in checbox_names:
checkbox = selenium.find_element(By.XPATH, f'//span[@class="rct-title" and text()="{name}"]')
ActionChains(selenium).move_to_element(checkbox).click().perform()
def test_checkbox_check_all(selenium, get_page_and_expand):
click_checkbox(
selenium,
'Notes', 'Commands', 'React', 'Angular', 'Veu','Public',
'Private', 'Classified', 'General', 'Word File.doc', 'Excel File.doc')
Should I ALWAYS use only fixtures?
If the code performs actions I would use functions. If it sets up data structures needed for the test, then a fixture.
I pretty much always use helper functions and @contextlib.contextmanager functions
so you don't use fixtures? why?
If the order of execution matters and it's not just a dependency tree: functions
I'm not keen on them, just prefer plain functions
Hello I have an application that can calculate p value and display it on graph(statistics) it works as follows: It is made in python as an addin to Excel. In Excel, there is a Calculate button , which, when the opens a GUI into which he enters data And at the end of that, the GUI button is generated, then the distribution graph is generated based on the input parameters entered by the user. My problem is that i need to write a document about Verification and Validation of this software, and im completely lost. I need help or some inspiration how can i make Validation and Verification of this software. Could you help me with that? I posted it here because it is something with testing
Need? Why?
project in school to make that app and write about validation and verification as last chapter of documentation
hmm. yea ok. Well I would start by just having some tests. Make up some data, check you get the output you expect.
what kind of tests because i tested the app many times but the test were just trying for example if the output is corret number or graph or if i get error message if i input wrong character etc..
Automated tests. Not manual tests.
My problem is that i need to write a document about Verification and Validation of this software, and im completely lost. I need help or some inspiration how can i make Validation and Verification of this software. Could you help me with that? I posted it here because it is something with testing
Could u copy paste(optionally screenshot) wording of this exercise part for clarity?
So we could be sure we help with smth u a actually asked about
As far as I remember my student years verification and validation were usually screenshots of program working and checking with smth its correctness by adding words if necessary
U could try selling unit/automated tests as alternative instead though... But i am not sure if your professors even know about unit testing existence. So not 100% sure if it is what they wish
my professor definetely doesnt know about unit testing it doesnt have to be unity testing in my opinion i dont really know what.. I had to develop app that can calculate and display p value and the app should be working as Excel addin. And in word im writting about the process of making it. This 5 key things should i add to my work(writting): 1. Describe the general principles of hypothesis testing.
2. Perform an analysis of current software solutions for p-value calculation and display.
3. Design and implement an application that will calculate and display the p-value for selected hypothesis tests and be executable as an Excel add-in.
4. Verify and validate the created application.
5. Evaluate the contribution of the created application. I dont know how it is in your country its like bachelor thesis
and i have everything but the validation and verification i am stuck on this and dont know how to formulate it
Just add to your document screenshots of program working π
Add to screenshots optionally some wordings/formulas proving it shows correct working.
Also as another way to proceed. University is about communicating...
if professor is not an option to ask, then your classmates (or even senior students) can be interrogated regarding how they did it.
good student... is sneaky student π
is there a way to force pytest's capsys to behave the same as when -s is passed in the CLI?
I found this but im not using argparse or a CLI to begin with so I cant find a solution https://stackoverflow.com/questions/76671641/make-pytest-capsys-fixture-treat-stdout-the-same-regardless-of-whether-or-not
How do I test those links in Python Selenium?
https://demoqa.com/links
<a id="created" href="javascript:void(0)">Created</a>
Do I need requests?
"test"? what do you mean?
check if links respond with corresponding statuses
Well it's not really a link....
actually I don't care what it is :p.. so how to test this api call
That's not an API call is my point. It's just a DOM node.
This is what I have just tried.
Getting TypeError: window.performance.getEntriesByType(...)[0].response is undefined
def test_elements_links_created(selenium):
selenium.get(urls.ELEMENTS_LINKS)
link = selenium.find_element(By.ID, "created")
link.click()
# result_output = selenium.find_element(By.ID, "linkResponse")
WebDriverWait(selenium, 30).until(
EC.text_to_be_present_in_element(
(By.ID, 'linkResponse'),
"Link has responded"
)
)
status_code = selenium.execute_script("return window.performance.getEntriesByType('navigation')[0].response.status")
status_text = selenium.execute_script("return window.performance.getEntriesByType('navigation')[0].response.statusText")
assert status_code == 201
assert status_text == 'Created'
navigation entry has only diagnostic performance data π¦ π¦ π¦
Any ideas?
You shouldn't use javascript: links, you should disable inline scripts using a content security policy
demoqa is not my page. I write tests to learn testing
So I guess it is testable
Hi guys
i use unittest
never dive too deep on pytest, only in some open source gigs
what are you thoughts on each?
Unittest is weirdly java-like and not pythonic. Pytest is much better except there is some overhead.
pytest can do everything unittest can do. literally it can all unittest tests for easy migration
but at the same time pytest can do more
my favourite feature is easy controlling scope of fixture reusability in pytest. besides for every test, calling for every module scope, or only once for session
Really powerful stuff to speed up tests
also simplified "assert" rules
hammett has lower overhead than pytest and is compatible to a tiny degree :) (I'm the author)
pytest has entire ecosystem today π for example pytest-django with its awesome features to assert amount of SQL queries by its ORM
or pytest celery integration to have in memory celery worker stuff
Works with hammett ;)
yeah, that was my ideia of it after spending the whole week at work writing unitest tests
would prefer pytest for sure
seems cool π₯
will check that out
if desired pytest supports grouping your own tests to classes too. (and even having fixtures running per class)
it is not obligating to use functions only ^_^
very rarely used pytest feature though
I think it's weird to do that. If you want to group, use files.
in general yes. i like strategy
somecode.py
somecode_test.py
Where u have in file just enough tests to test the code of the relevant file
if necessary i can split tests to more files, testing only certain thing
This approach is also great if u generate documentation via sphinx also
relevant tests serving as code examples can be embedded into generated docs as entire file contents
:incoming_envelope: :ok_hand: applied timeout to @woeful terrace until <t:1714949266:f> (10 minutes) (reason: duplicates spam - sent 4 duplicate messages).
The <@&831776746206265384> have been alerted for review.
!unmute 879218993544720424 Please don't say hi in random channels, these all have specific discussion topics. I'd suggest #python-discussion or one of the off-topic channels
:incoming_envelope: :ok_hand: pardoned infraction timeout for @woeful terrace.
ok sorry
How can I override/monkeypatch/decorate selenium.get() method in fixture so it reloades page if there is 502 Bad Gateway in page's source?
What do you think about this idea?
@pytest.fixture
def selenium(selenium):
selenium.implicitly_wait(10)
selenium.maximize_window()
return selenium
This is example of my .get() usage (I start the most tests with it)
def test_dynamic_properties(selenium):
selenium.get(urls.ELEMENTS_DYNAMIC_PROPERTIES)
text = selenium.find_element(By.XPATH, '//p[text()="This text has random Id"]')
enable_after_btn = selenium.find_element(By.ID, "enableAfter")
color_change_btn = selenium.find_element(By.ID, "colorChange")
color_change_btn_class = color_change_btn.get_attribute("class")
Or is there some better way to handle this 502 for all tests?
mistake π¦ thought driver.get() returns root element
Can anyone please tell what changes should I make in order to make it work in this way :
addressIn gets executed,
if unserviceableAddress is found
use addressRe to do the following - convert the pincode into an int, add 1 to it, and then convert it back to string, and use it like addressIn
if unserviceableAddress is found again, print -- doesn't deliver at your location
and if unserviceableAddress is NOT found, print delivery available, execute blinkSearch()
while True:
pincode = input("Enter Pincode : ")
if pincode.isdigit():
break
else:
print("Invalid Pincode. Please enter only digits.")
productName = input("Enter Product Name : ")
driver = webdriver.Firefox()
driver.get("")
driver.implicitly_wait(2)
def addressIn():
addressBar = driver.find_element(By.XPATH, '')
addressBar.clear()
typeSim(addressBar, pincode[:3])
reAddressBar = driver.find_element(By.XPATH, '')
typeSim(reAddressBar, pincode[-3:])
addressSuggestion = driver.find_element(By.XPATH, '')
addressSuggestion.click()
def addressRe():
pincodeRe = int(pincode)
pincodeRe = pincodeRe+1
pincodeRe = str(pincodeRe)
addressBar = driver.find_element(By.XPATH, '')
addressBar.clear()
typeSim(addressBar, pincodeRe[:3])
reAddressBar = driver.find_element(By.XPATH, '')
typeSim(reAddressBar, pincodeRe[-3:])
addressSuggestion = driver.find_element(By.XPATH, '')
addressSuggestion.click()
def unserviceableAdressLocated():
unserviceableAddress = WebDriverWait(driver, 3).until(
EC.presence_of_element_located((By.XPATH, '')))
addressIn()
reAttempts = 1
for _ in range(reAttempts):
try :
unserviceableAddress = WebDriverWait(driver, 3).until(
EC.presence_of_element_located((By.XPATH, '')))
addressRe()
if unserviceableAdressLocated():
print("Sorry, -- doesn't deliver at your location.")
driver.quit()
break
except (TimeoutException, NoSuchElementException):
print("Delivery available at your location, fetching relevant results...")
blinkSearch(driver)
You gave many steps. Which step is the first problem?
the problem lies in the last part (starting from reattempts)
i've been unable to use addressRe() in the desired manner.
apologies if my query isn't clear, lmk and i'll try to reframe it.
Still not clear. You have a big function. Which part is the problem?
i doubt if the function is directly causing problems. the issue lies in the exception handling part
addressIn()
is supposed to return unserviceableAddress()
if the element unserviceableAddress() NOT found, then blinkSearch() must execute.
and if unserviceableAddress() is found,
then addressRe shall execute once,
if unserviceableAddress() is found again after the execution of addressRe
it should print "doesn't deliver to your location"
and exit
You keep saying many things. You need to take one thing at a time. This is programming. Many things at the same time is not programming. It is confusion.
my apologies, i'll try to simplify once again
addressin, enters the pincode, it checks if the address is serviceable by finding an element.
if the element is not found, this means that the address is serviceable and we can proceed with the other operations
but
if we find that element which says that the address is unserviceable, we try to manipulate the pincode, and retry entering the address ONCE using reAddress
and if we get that element again, we give up and print the error message
yea, you are again talking about a thousand things
which LINE is the problem NOW? just ONE line
the for loop and try except block
no, that's many things. ONE thing
try except block
You said the problem was with addressIn. There is no try/except in there. Also, I said ONE LINE, you said "the block"
And you still haven't said anything about any problem.
Ask something specific Before asking for help, try to make the smallest example you can of the problem. Donβt just show us your entire code base. If you donβt understand the advice given, say so!. Donβt just ignore it. Donβt tell us error messages in your own words. Copy paste them. In full. βDoesnβt workβ is not an error message. How does it no...
Please read that
sorry for being vague.
No need to apologize. This is normal when you are a beginner. I'm trying to show you how to think to be able to ask a good question. If you learn this, you can get help fast. Often you can even solve the problem yourself.
the addressRe function isn't executing as expected.
You haven't told anyone what happened. You haven't told anyone what you expect.
addressin enters pincode
tries to find an element
if the element is found,
addressRe should enter the pincode after making changes in it.
but it isn't doing so.
Is the element found?
yes, and the code stops at that point because nothing happens past that.
So, if that works, why are you saying it? That's not a part of the problem. See? You are saying a ton of irrelevant things.
typeSim(addressBar, pincodeRe[:3]) <- this is the line that is not filling in the data?
I asked you above for ONE line. Is this the line?
yes
how do you know it found the element?
before this overcomplication, i simply printed a msg "undeliverable location" when the element was found.
that doesn't answer the question
How do you know?
and the xpath of the element remains same so it definitely finds the element
the code you showed has '' as the xpath, I assume this is something you removed when asking?
yes
Ok. Now. AGAIN. How do you know? How did you check?
earlier, i used to find that element using the same "presence_of_element_located" method, and print the error message if it was found.
So I'm hearing is that you checked something before. This means you do not know. You should change the xpath by adding some random character at the end and see if it changes behavior. You must make sure of the basics.
did this. the previous xpath was correct and indeed it was finding the right element
What happened when the xpath was wrong?
it tried to proceed with the next function but gave hell lotta errors because the element where the next function is supposed to enter text didn't load (this sounds complicated af, but in my understanding it worked like it's supposed to)
I can't help you if you ignore everything I say
you asked, what happened when i changed the xpath of the element i am supposed to find
to which i answered (with weird sentencing), it didn't find that element, resulting in the code proceeding forward and breaking
The code proceeding? You don't get an exception?
i mean it tried to execute blinkSearch() and during handling the exception, another exception occured
so it broke
Ok, so you have broken code in the except block. Maybe delete that first. Secondly, delete that loop. Make the code simple first. You have a lot of code that just makes you more confused.
See, you should have said that you get an exception if the xpath is incorrect. That's all I wanted to hear.
Ok, so moving on...
What is that typeSim function? Is that something you wrote?
typesim is meant to emulate human typing behaviour
def typeSim(element, text, delay=0.05):
for character in text:
ActionChains(driver).move_to_element(element).click().send_keys(character).perform()
time.sleep(delay)
oh dear...why are you doing all that?
because it won't show me the address suggestions if i directly send keys
and it's compulsory to select the address from the dropdown menu
And why aren't you injecting all the characters at once and then waiting after to get the dropdown?
did that before, the dropdown won't load up for some reason
hmm.. ok. so.. do you get any characters in the input field?
characters in the input field? i didn't understand
You are trying to make the program "type" into an input field right?
yes
do you know what "character" means?
ok, so you try to type say "foo".. do you end up with "f"? "fo"? "foo"? or just ""?
foo
so what you meant here was in fact "no"?
SORRY, you're right. i meant "NO".
i didn't read it properly π
Well.. that was a wasted hour
Ok, so I still have no idea what the problem is. After 2 hours. No idea.
Do you see how this is a problem?
i get it. i really need to rework on a lot of things (esp how to explain things), so until then i'll try to figure this out by myself
hey guys, beginner dev here
I've been stuck with this test for an entire day, relatively complex function (for me) and beginning to waste my time
mocking is a huge time sink. At what point do you just move on?
I often feel it would be much simpler to remove mocking and just test every single thing in real-world, it would simplify some things a lot
I tend to avoid mocking by making the underlying function testable
Eg by separating IO from business logic
But sometimes it's better to write a small test that does real IO
building on graingerts reply: I think it's ok to just leave some shell stuff untested. Make the majority of the code pure and test that, and then if there are 10 lines of imperative IO stuff that calls the tested stuff that you run manually that's fine.
Testing is a tradeoff. Don't treat 100% coverage as a religion, you will make life miserable.
Thanks guys for replying
this really hits home
I'd prefer a suite of small integration tests over large unit tests
last app I did I spent sometimes days trying to pass one or two tests, and while this really ironed out most of the things, I was really miserable
What's the system under test?
I do small unit tests honestly, I break everything down. It goes relatively fast, but I'm now just getting deper with Celery and Redis, and I just can't figure it out
I have zero mocks in my current code base. 3 years building it full time solo. ~50k dry lines.
think I'll remove mocking for this one
What were you having trouble with?
and see how it goes
there were many things. I was a complete beginner in actually unit-testing for that app, so you can well imagine
the moment complexity popped up in central modules I was way over my head
What did your mocks look like?
Celery has a test mode (always eager) that you can use
I'm actually right now trying to test a celery task
gotta get back to it now honestly, getting a bit tired as it is
time is precious
thanks for the advice
And you can't call it like any function?
I can, I just am not familiar with mocking Redis to avoid connection, plus some functions aren't being called
and because I mocked REdis, Celery, some other connection, some other celery task, and it's all just a complicated bunch π
my biggest issue is when to actually make a change in the codebase and whether I did it just to pass the test, or is it an actual improvement..
No I mean.. the task is after the celery/redis thing is already done, so should run without all that no?
I suppose. I wanted to test some handling in a few error cases first
but I realize now that may not be the best thing to get to MVP any time soon
it's just a very complicated module, for me. It's a bunch of Celery tasks, which is all relatively new to me
eh. u should not mock it.
Just raise it locally via docker-compose and use it as testing version.
And celery has special pytest fixtures to run in pytest in memory
https://celeryproject.readthedocs.io/zh-cn/latest/userguide/testing.html
def test_create_task(celery_app, celery_worker):
@celery_app.task
def mul(x, y):
return x * y
assert mul.delay(4, 4).get(timeout=10) == 16
they are activated like this
Maybe you don't need/want celery at all too heh. I think most people don't really, they just want a better cron.
having programmatic cron as a code with single entrypoint for application is really cool π
better than having an array of entrypoints
i think as better solution we can be only having just for loop second thread... that works great in languages that have real parallelism at least.
subprocess can work in python though pretty fine (without shared memory, but oh well, at least like this)
I wrote Urd to be a simple scheduler. Avoids multiple overlapping executions like you can get with cron, has scheduling <1m, and can keep processes alive to avoid spending all the time in startup.
Works very well for me.
That can work as well.
programmatic cron but just customized standartized to a library ^_^
taking care of all boilerplating
logical next step if doing customizations regarding that too many times
The only little flaw with this logic is... mm...
your Urd is perfect for you, you are maintainer of it π so u can be sure it is 100% perfect fit for you and author(you) will adjust it for you.
Celery makes sense to choose for him because it has solid team of devs maintaing it and he can be sure it will be maintained as well.
at a level of how Urd is having small team of contributor, it will make sense for him to choose it only if he will like its code and will feel confident in ability to write Pull Request / Fork it and continue its development on his own if necessary.
Otherwise... may be a point to write his own "Urd"
imo you want a scheduler, and then bring your own queue.
def some_task():
for thing in Thing.objets.filter(something=3):
update_thing(thing)
much more robust, simple, maintainable
If it does the wrong thing, it doesn't matter if is well maintained π€£
But yea, Urd is tiiiiiny. And MIT or some such license. Can fork it and make it private for all I care.
i cleared all the complicated stuff, broke it into small chunks for my own understanding, and finally fixed it. thanks a lot for you help.
Yea, breaking it up into smaller pieces and checking them one by one is how you make progress pretty much.
yo
Depending on the testing framework you use, you can mock celery. For me, I used unittest and was able to setup celery tests in the setup Class and teardown class and the mocked the actual celery function to return none then manually called the celery function within the rest case, awaited it's response and was able to test it. That's a long process but if you still need it tested that is a probable approach
And depending on whichever framework you are using, mine was Django and I had to use a Transaction Test Case instead
Hi, thanks for replying. I already managed to mock both celery and redis and tested a lot of stuff. I'm now trying to test some complicated function, and it's the first time I had to split the functionality into 3 tests leveled per complexity. It's a complicated stuff, but it does help me iron out and improve the code eventually.
I just want to test this as soon as possible and move to real-world testing, and integration testing. It's taking a lot of my time
good to know. I'm using Flask
Great to know you've figured it out
Oh, that doesn't apply then
Not sure what I am doing wrong but for some reason assertions are not failing the test for me.
I am writing unit-tests for an existing project and running everything at once like so:
# Run osc unit tests
osc_test_suite = unittest.TestLoader().discover('unit_tests/osc_tests')
unittest.TextTestRunner().run(osc_test_suite)
# Run RTMP unit tests
rtmp_test_suite = unittest.TestLoader().discover('unit_tests/rtmp_tests')
unittest.TextTestRunner().run(rtmp_test_suite)
# Run utility unit tests
utility_test_suite = unittest.TestLoader().discover('unit_tests/utility_tests')
unittest.TextTestRunner().run(utility_test_suite)
osc_test_suite, rtmp_test_suite runs fine but I am having issues with utility_test_suite. It is able to discover all 16 tests within it and it also running them. I have verified both by printing the same. I have also verified the assertion that's supposed to fail is not equal but printing it.
Not sure where I am going wrong and would appreciate some help.
One interesting thing. While running tests, for utility_test_suite it's not logging the (......):
poetry run python3 run_unit_tests.py
.............
----------------------------------------------------------------------
Ran 13 tests in 0.008s
OK
.......Exception in socket.connect():
..Exception in socket.connect():
...................................................
----------------------------------------------------------------------
Ran 60 tests in 2.548s
OK
Name Stmts Miss Cover
----------------------------------------------------------------------
something definitely seems amiss with the structure of your tests. Why are you writing your own loading and running code? Why not use python -m unittest discover to do all that instead?
btw, it looks like you have coverage.py involved there also, but I don't see how that's being invoked. Do you have a link to your project we can look at?
we needed coverage so we ended up running unittests like so:
# pip install coverage
import unittest
import coverage
# Start code coverage
cov = coverage.Coverage()
cov.start()
# Run osc unit tests
osc_test_suite = unittest.TestLoader().discover('unit_tests/osc_tests')
unittest.TextTestRunner().run(osc_test_suite)
# Run RTMP unit tests
rtmp_test_suite = unittest.TestLoader().discover('unit_tests/rtmp_tests')
unittest.TextTestRunner().run(rtmp_test_suite)
# Run utility unit tests
utility_test_suite = unittest.TestLoader().discover('unit_tests/utility_tests')
unittest.TextTestRunner().run(utility_test_suite)
# Stop code coverage
cov.stop()
cov.save()
# Generate code coverage report
coverage_percent = cov.report()
# Fail if total code coverage is less than 90%
assert coverage_percent >= 90, f"Code coverage is {coverage_percent}%. Minimum required is 90%."
# Generate code coverage HTML report (optional)
# cov.html_report(directory='unit_tests_coverage_report')
hence not using python -m unittest discover
There you go:
https://gitlab.com/avilabss/insta360
OK, why not: coverage run -m unittest discover; coverage report --fail-under=90; coverage html
just wasn't aware of doing it this way since the existing implementation was working just fine until now. Will try this way now, thanks π
i don't know why your third suite wouldn't run, it looks similar to the others.
@ripe iris btw, if your setUp and tearDown don't do anything, you can omit them: https://gitlab.com/avilabss/insta360/-/blob/main/unit_tests/utility_tests/test_insta360_cmd.py?ref_type=heads#L37-41
it does run actually but assertions won't work for some reason (even when they should). Again, something is off, not seeing log for third suite either (....)
how do you know the tests are running?
good ol print('xyz'), did it for a few tests and it showed up
coverage run -m unittest discover -s unit_tests/utility_tests; coverage report --fail-under=90;
this works! but still would be nice if I could make it work in the existing way
i guess it will take some debugging then to see what's going on. Something about the output is being suppressed.
oddly enough, I moved 3rd suite to 1st position and now it works. No clue why, how, etc
all i can think is that something in a test is patching something too hard. A quick scan doesn't reveal it though.
What do you mean "doesn't fail the tests"? You mean the exit code for the entire run isn't failure?
Example:
self.assertEqual(result.message_code_str, 'PHONE_COMMAND_GET_FILE_LISTTTT')
Here, result.message_code_str is PHONE_COMMAND_GET_FILE_LIST still it won't raise assertion
Can you reproduce this with self.assertEqual(1, 0)?
I feel like you're showing way too much code, making it impossible for anyone else to reproduce it
ok, let me check
def test_reproduct(self):
print('reproduct start')
self.assertEqual(0, 1)
print('reproduct end')
Something interesting:
----------------------------------------------------------------------
Ran 60 tests in 2.573s
OK
reproduct start
Name Stmts Miss Cover
----------------------------------------------------------------------
insta360/osc.py 88 27 69%
reproduct end didn't show up, so test got executed and failed but didn't show up on console for some reason?
I don't remember how unittest works. How does it normally look on failed test?
something like this:
...........reproduct start
F
======================================================================
FAIL: test_should_fail (test_packet_analyzer.TestPacketAnalyzer.test_should_fail)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/avi/Development/Upwork/insta360/unit_tests/utility_tests/test_packet_analyzer.py", line 55, in test_should_fail
self.assertEqual(1, 2)
AssertionError: 1 != 2
----------------------------------------------------------------------
Ran 12 tests in 0.009s
FAILED (failures=1)
.............
That's without your special runner wrapping?
with*
it works the same way without it
for now, I have just put that suite at the top and everything seems to work again
Im currently working on a django project and Iβm trying to test features by using pytest, every time I run pytest this error come up, can someone help me please. `=================================================ERRORS==================================================
__________________________________ ERROR collecting appsauthstests.py___________________________________
env/lib/python3.10/site-packages/django/apps/registry.py:158: in get_app_config
return self.app_configs[app_label]
E KeyError: 'auth'
During handling of the above exception, another exception occurred:
env/lib/python3.10/site-packages/django/contrib/auth/init.py:188: in get_user_model
return django_apps.get_model(settings.AUTH_USER_MODEL, require_ready=False)
env/lib/python3.10/site-packages/django/apps/registry.py:208: in get_model
app_config = self.get_app_config(app_label)
env/lib/python3.10/site-packages/django/apps/registry.py:165: in get_app_config
raise LookupError(message)
E LookupError: No installed app with label 'auth'.
During handling of the above exception, another exception occurred:
apps/auths/tests.py:9: in <module>
User = get_user_model()
env/lib/python3.10/site-packages/django/contrib/auth/init.py:194: in get_user_model
raise ImproperlyConfigured(
E django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'auth.User' that has not been installed`
You don't have auth in installed apps?
Yes I do, `Setting.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_simplejwt.token_blacklist',
'rest_framework.authtoken',
'drf_yasg',
'apps.auths',
'apps.socialAuths',
'apps.product',
'colorfield',
]
AUTH_USER_MODEL = 'auths.CustomUserβ
model.py
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, max_length=100)
username = models.CharField(null=True, blank=True, max_length=100)
first_name = models.CharField(null=True, blank=True, max_length=100)
`
So pytest is thinking you have a different AUTH_USER_MODEL
Exactly, I donβt understand..have you come across before ?
It looks like you have a different settings module configured with pytest
What does your pytest config look like?
`[pytest]
DJANGO_SETTINGS_MODULE=main.settings
python_files = tests.py test_*.py *_tests.py`
`(env) envCxntStop Server %pytest
=========================================== test session starts ===========================================
platform darwin -- Python 3.10.11, pytest-8.2.0, pluggy-1.5.0
django: version: 5.0.4, settings: main.settings (from ini)
rootdir: /Users/β¦/Server
configfile: pytest.ini
plugins: django-4.8.0
collected 0 items / 2 errors
=========== ERRORS ==========
______ ERROR collecting apps/auths/tests.py _______
env/lib/python3.10/site-packages/django/apps/registry.py:158: in get_app_config
return self.app_configs[app_label]
E KeyError: 'auth'
During handling of the above exception, another exception occurred:
env/lib/python3.10/site-packages/django/contrib/auth/init.py:188: in get_user_model
return django_apps.get_model(settings.AUTH_USER_MODEL, require_ready=False)
env/lib/python3.10/site-packages/django/apps/registry.py:208: in get_model
app_config = self.get_app_config(app_label)
env/lib/python3.10/site-packages/django/apps/registry.py:165: in get_app_config
raise LookupError(message)
E LookupError: No installed app with label 'auth'.
During handling of the above exception, another exception occurred:
apps/auths/tests.py:9: in <module>
User = get_user_model()
env/lib/python3.10/site-packages/django/contrib/auth/init.py:194: in get_user_model
raise ImproperlyConfigured(
E django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'auth.User' that has not been installed
==== short test summary info =====
ERROR apps/auths/tests.py - django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'auth.User' that has not ...
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
======= 1 errors in 1.32s ====`
And what does tree show?
Ah
I think you called get_user_model too early. You should just use auths.CustomUser
Sorry what do you mean , can you give me an example sorry for the inconvenience
A list of countries Iβve never been too lol
Show your code for apps/auths/tests.py
It's supposed to show your project directory tree
`from django.contrib.auth import get_user_model
from django.test import TestCase
from rest_framework.test import APITestCase
from rest_framework import status
from django.urls import reverse
User = get_user_model()
class UserModelTestCase(TestCase):
def test_create_user(self):
user = User.objects.create(
first_name ='Test1',
last_name='Last1',
email='test@gmail.com',
username='Test123',
password='Test123',
date_of_birth='1995-12-16'
)
self.assertEqual(user.first_name, 'Test1')
self.assertEqual(user.last_name, 'Last1')
self.assertEqual(user.email, 'test@gmail.com')
self.assertEqual(user.date_of_birth,'1995-12-16')
self.assertTrue(user.is_active)
with self.assertRaises(ValueError):
User.objects.create_user(username='', email='', password='')
`
Remember to use ```python
On line 9 you have a call to get_user_model()
Use auths.CustomUser instead
I think pytest-django only configures Django after collection
from rest_framework.test import APITestCase
from rest_framework import status
from django.urls import reverse
from .models import CustomUser as User << like this ?
class UserModelTestCase(TestCase):
def test_create_user(self):
user = User.objects.create(
first_name ='Test1',
last_name='Last1',
email='test@gmail.com',
username='Test123',
password='Test123',
date_of_birth='1995-12-16'
)
self.assertEqual(user.first_name, 'Test1')
self.assertEqual(user.last_name, 'Last1')
self.assertEqual(user.email, 'test@gmail.com')
self.assertEqual(user.date_of_birth,'1995-12-16')
self.assertTrue(user.is_active)
with self.assertRaises(ValueError):
User.objects.create_user(username='', email='', password='')
If so new error
================== ERRORS ================
__________ ERROR collecting apps/auths/tests.py ______
from .models import CustomUser as User
apps/auths/models.py:1: in <module>
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
env/lib/python3.10/site-packages/django/contrib/auth/models.py:5: in <module>
from django.contrib.contenttypes.models import ContentType
env/lib/python3.10/site-packages/django/contrib/contenttypes/models.py:139: in <module>
class ContentType(models.Model):
env/lib/python3.10/site-packages/django/db/models/base.py:134: in __new__
raise RuntimeError(
E RuntimeError: Model class django.contrib.contenttypes.models.ContentType doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
========= short test summary info ============
ERROR apps/auths/tests.py - RuntimeError: Model class django.contrib.contenttypes.models.ContentType doesn't declare an explicit a...
Check if you have missing migrations or db got out of sync with your models. I have found this problem and that's how I've fixed it
Do you call django.setup() somewhere?
All looks good from here
No
I guess try to make a MRE and we can try debugging that
What does main/settings.py looks like?
import os
from pathlib import Path
from dotenv import load_dotenv
from datetime import timedelta
load_dotenv()
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_simplejwt.token_blacklist',
'rest_framework.authtoken',
'drf_yasg',
'apps.auths',
'apps.socialAuths',
'apps.product',
'colorfield',
]
AUTH_USER_MODEL = 'auths.CustomUser'
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'main.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'main.wsgi.application'
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=10),
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
}
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
'NON_FIELD_ERRORS_KEY': 'error',
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '1000/day',
'user': '500/day'
}
}
# Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get("DB_NAME"),
"USER": os.environ.get("DB_USER"),
"PASSWORD": os.environ.get("DB_PASSWORD"),
"HOST": os.environ.get("DB_HOST"),
"PORT": os.environ.get("DB_PORT"),
}
}
# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
STATIC_ROOT = 'staticfiles'
import os
from .base import * # noqa
from dotenv import load_dotenv
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
Just main/settings.py
Remember to use ```python
If you want to upload multiple files use
!paste
If your code is too long to fit in a codeblock in Discord, you can paste your code here:
https://paste.pythondiscord.com/
After pasting your code, save it by clicking the Paste! button in the bottom left, or by pressing CTRL + S. After doing that, you will be navigated to the new paste's page. Copy the URL and post it here so others can see it.
@night arrow ^
import os
from pathlib import Path
from dotenv import load_dotenv
from datetime import timedelta
load_dotenv()
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_simplejwt.token_blacklist',
'rest_framework.authtoken',
'drf_yasg',
'apps.auths',
'apps.socialAuths',
'apps.product',
'colorfield',
]
AUTH_USER_MODEL = 'auths.CustomUser'
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'main.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'main.wsgi.application'
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=10),
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
}
You said that was in base.py before
Show the output from tree?
What's in settings/__init__.py
βββ main
β βββ __init__.py
β βββ __pycache__
β β βββ __init__.cpython-310.pyc
β β βββ urls.cpython-310.pyc
β β βββ wsgi.cpython-310.pyc
β βββ asgi.py
β βββ settings
β β βββ __pycache__
β β β βββ base.cpython-310.pyc
β β β βββ dev.cpython-310.pyc
β β βββ base.py
β β βββ dev.py
β β βββ test.py
β βββ urls.py
β βββ wsgi.py
βββ manage.py
βββ pytest.ini
βββ requirements.txt
βββ staticfiles
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
from dotenv import load_dotenv
load_dotenv()
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', os.environ.get('DJANGO_SETTINGS_MODULE'))
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
What do you have in DJANGO_SETTINGS_MODULE?
export DJANGO_SETTINGS_MODULE="main.settings.devβ
Basically you're pointing pytest at main.settings which is empty
you need to point it at main.settings.dev or test
Changed it now , it now works thank you for being patient and helping
Anyone experienced in php unit testing i need some help
This is a python server. You are in the wrong place.
Anyone know how I can disable certain aspects of Pylint?
what aspects? It's very configurable.
Missing docstrings on functions and functions, lines to long
You can disable any violation you like. for example: https://github.com/nedbat/coveragepy/blob/master/pyproject.toml#L62-L101
Thank you!
hey quick question. i am new to coding and tried to code blackjack. its running but there is a problem. my ace is only worth 1 instead of either 1 or 11. my code is definitely not the best but hopefully you can help me with that
forgot to mention that its manly in german
You should get in the habit of coding in English always. Code is in English, that's just how it is.
...and this isn't testing related as far as I can see?
Hello guys,
for below piece of code
for file in list_of_files:
try:
shutil.move(src_path, dest_path)
except PermissionError:
print(f"Permission error copying {file} from {self.source_path} to {self.destination_path}")
How should I write a test in pytest? I come with this soultion:
def test_move_no_permissions(self, file_instance):
"""Test move method when user has no permissions in destination folder."""
os.chmod(file_instance.destination_path, 0o400)
I know there is no assert statement, but the test passes due to the except condition is catching PermissionError
Move or copy? The error says copy but the code says move. Your problem isn't automatic testing ;)