-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use pytest, put tests into separate dir #70
base: master
Are you sure you want to change the base?
Conversation
Refs: pyenv/pyenv-virtualenv#206 Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Is the init.py in https://github.com/bastikr/boolean.py/pull/70/files#diff-498cf53d35427897613cdfc4b76fc6ea really needed? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some comments. The thing that makes it quite hard to review is that most everything has changed in the tests.
I am ok with using py.test as it makes things cleaner, but being able to review the diff of a test before and after would make much more sense to review anything correctly.
Do you think you could merge it all back as a single test file (and keep the original ordering of the tests) ...?
Once that is done, splitting in files would be a non issue, so as they say flat is better than nested
tests/test_not.py
Outdated
|
||
assert a == a.simplify() | ||
assert a == (~~a).simplify() | ||
assert a == (~~ ~~a).simplify() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you injected a space here and may be in a few other ~~
places... likely a side effect of your code editor or formatter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, no, that space was intentional. It was there before:
test-requirements.txt
Outdated
@@ -1 +1,4 @@ | |||
tox==2.7.0 | |||
pytest==3.1.2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason why you need such strict pinned requirements?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, not really. Just in the spirit of exactly pinning dependencies. Later on, one could outsource dependency updating to a bot like https://pyup.io/ which will create PRs when a dependency could be updated. However, can change to 3.1.x
or even 3.x.x
(not sure about the exact syntax though)
setup.py
Outdated
''' | ||
|
||
with open('README.md') as readme: | ||
long_description = readme.read() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use an .rst file and use restview to check that it works fine with Pypi?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is true, will rewrite the readme in .rst
and check it then.
tests/test_not.py
Outdated
assert (~(a & a & a)).simplify() == (~(a & a & a)).simplify() | ||
assert (~(a | a | a)).simplify() == (~(a | a | a)).simplify() | ||
|
||
@pytest.mark.xfail(reason="a.cancel() should work but does not") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have changed the logic of this test.
It was:
def test_cancel(self):
algebra = BooleanAlgebra()
a = algebra.Symbol('a')
self.assertEqual(~a, (~a).cancel())
self.assertEqual(a, algebra.parse('~~a').cancel())
self.assertEqual(~a, algebra.parse('~~~a').cancel())
self.assertEqual(a, algebra.parse('~~~~a').cancel())
So I am not sure cancelling a symbol makes any sense. Also the logic to parse vs. constrict Python objects is rather different... so in changing the test you are not testing the same thing (eventually both would be good, though a primary use case is with a parsed input)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thinking process there was:
there is a separate test that deals exclusively with
parse
and also parsing is never easy, so let's throw it out of this test and just use python objects
I also started a separate test_parse.py
later, moving a small portion of test_parse
from OtherTestCase
. The move is not finished yet though.
Resolution: I will write a second version of test_cancel
that uses parse
then.
Sure, I can first do the changes in the same file and in the same order so that it would be easier to review. However, please note that you could:
Anyway, I totally understand that this PR is big/hard to follow. As you suggest, I can use it to quick-start another PR that would first do the changes and then (once you've seen the exact changes), splits the files. |
Removing the file and runnign |
* Use "README.md as long_description" common practice * Fix license name: LICENSE is *simplified*, not *revised* BSD, see: https://en.wikipedia.org/wiki/BSD_licenses Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Use link below to verify setup: https://docs.pytest.org/en/latest/goodpractices.html Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
And I merged back the Did not merge back Also did not merge back Also rewrote Update: the things I did not do yet:
|
Even though some places recommend fixing dependencies [1], if you perform a github search [2] you will see that those repositories do *not* pin their versions. So, unpinning versions here. [1]: https://devcenter.heroku.com/articles/python-pip#best-practices [2]: https://github.com/search?utf8=%E2%9C%93&q=language%3Apython+stars%3A%3E1000 Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Test .cancel() on both Python variables and .parse() results [1]: #70 (comment) Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Everything has been addressed, I am removing the |
author='Sebastian Kraemer', | ||
author_email='basti.kr@gmail.com', | ||
url='https://github.com/bastikr/boolean.py', | ||
packages=find_packages(), | ||
include_package_data=True, | ||
zip_safe=False, | ||
test_loader='unittest:TestLoader', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous test suite could run equally well with or without pytest. Any special reason to make py.test a hard requirement for tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to force everybody to use the same thing. The more options you have, the more (compatibility?) things you have to keep in mind. With just one thing there is only one way: pip install -r requirements.txt; pytest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I know this is a very old PR, but...)
The use of @pytest
decorators makes pytest a hard requirement, full stop. (And I agree with @alisianoi: it's better not to support multiple test frameworks, which lead to everyone executing the tests in subtly different ways. Do that, you've stopped testing your code; now you're compatibility-testing the frameworks instead.)
pytest-runner
is deprecated. (As are any use of test_runner
, test_suite
, tests_require
, etc. in setup.py
. setup.py
as a whole is deprecated, of course, but its test args are "super double deprecated".) These days, tox
would be a better choice as test runner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for remerging the tests: this was much easier to review!
algebra = BooleanAlgebra() | ||
self.assertEqual(algebra.TRUE, algebra.TRUE) | ||
BaseElement() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have missed carrying over this test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, you mean BaseElement()
, the empty call. So, it should test that an empty call does not trigger any sort of exception?
This is the same type of test that I just removed from https://github.com/bastikr/boolean.py/pull/70/files/29e66f0e82244f90608eaad9739cf0271cb9a983#r125046762
I will look up how to write a unit test that "makes sure this is legal" (or tell me if you know how) and bring this and the linked tests from other comment back.
tests/test_boolean.py
Outdated
assert algebra.FALSE == algebra.FALSE | ||
|
||
assert algebra.TRUE != algebra.FALSE | ||
assert not algebra.TRUE == algebra.FALSE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is not ==
the same as !=
? things are a tad complicated at times because of the overloading of dunder methods and the way Python boolean logic shortcircuiting works
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was writing like this first:
assert not (algebra.TRUE == algebra.FALSE)
and then noticed that the ()
can be dropped, so I dropped them. Will rewrite as:
assert algebra.TRUE != algebra.FALSE
assert not (algebra.TRUE == algebra.FALSE)
|
||
def test_init(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where have these tests gone?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I could tell, these were not tests: no assert statements of any kind. If they were to fail, that would be ugly tracebacks, not test results. Besides, that is just assignment under the hood, so don't see what is being tested.
On the other hand, the init
part of the test name is confusing and might suggest that this is part of some test setup, which it is not. So, removed.
tests/test_boolean.py
Outdated
|
||
def test_symbols_eq_0(self): | ||
algebra = BooleanAlgebra() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this may be a few too many empty lines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, not really. The blank lines show three parts of the unit test: setup, run and assertion. Can remove them if you don't like them, sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests/test_boolean.py
Outdated
algebra = BooleanAlgebra() | ||
|
||
assert algebra.Symbol('a') == algebra.Symbol('a') | ||
assert not algebra.Symbol('a') == algebra.Symbol('b') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment as above. I am not 100% sure that not ==
is the same as !=
here because of the (and, or, not) operator overloading
def test_symbols_eq_ne(self): | ||
algebra = BooleanAlgebra() | ||
|
||
symbols0 = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to prefer many small tests methods that a loop... Now if we keep a loop, this still might need exploding in different purpose tests? I find it a tad hard to read what the tests are really doing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, this is actually something that I like to do: a few manual single unit tests above (like the test_symbol_ne*
above) and then a final big test (that I also sometimes write as a test generator). If this big test starts to fail, then I find out which exact test case is failing and factor it out into yet another small, separate test_symbol_ne
test. Simply factoring everything into nice small tests is too much typing and repetetive test names :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But can do if you want, sure
tests/test_boolean.py
Outdated
def test_demorgan(self): | ||
assert a == a.simplify() | ||
assert a == (~~a).simplify() | ||
assert a == (~~ ~~a).simplify() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the space in the middle of the ~~ ~~
here and there is likely a typo that I introduced in the past from some editor reformatting.
tests/test_boolean.py
Outdated
assert parse('~(~a & b)').demorgan() == parse('a | ~b') | ||
assert parse('~(a & ~b)').demorgan() == parse('~a | b') | ||
|
||
# TODO: enforced order is obscure, must explain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean the precedence of operators? I agree, this is also obscure to me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant the lines 585
to 595
below that use the <
and >
comparison operators. Is that what you meant?
assert algebra.parse('((' + x + '))') == algebra.TRUE | ||
assert algebra.parse('(((' + x + ')))') == algebra.TRUE | ||
|
||
@pytest.mark.xfail(reason='IndexError indicates parsing problem') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remove the xfail
decorator, the parser breaks and the whole thing crashes with an IndexError
. That is not supposed to happen because the parser was designed to throw custom errors when there is a problem. So, you either throw your own errors or generic errors, not both (imho). Since it starts to throw generic errors all over sudden means there is likely some corner case that was not considered.
with pytest.raises(ParseError): | ||
parse(expression) | ||
|
||
@pytest.mark.xfail(reason='IndexError indicates parsing problem') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above. Which index error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When responding to comments, I misclicked the "Start review" button and now have to write a review summary?
Anyway, will address your comments in code, push and then enumerate the fixed/unaddressed things.
def test_symbols_eq_ne(self): | ||
algebra = BooleanAlgebra() | ||
|
||
symbols0 = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But can do if you want, sure
tests/test_boolean.py
Outdated
assert parse('~(~a & b)').demorgan() == parse('a | ~b') | ||
assert parse('~(a & ~b)').demorgan() == parse('~a | b') | ||
|
||
# TODO: enforced order is obscure, must explain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant the lines 585
to 595
below that use the <
and >
comparison operators. Is that what you meant?
assert algebra.parse('((' + x + '))') == algebra.TRUE | ||
assert algebra.parse('(((' + x + ')))') == algebra.TRUE | ||
|
||
@pytest.mark.xfail(reason='IndexError indicates parsing problem') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remove the xfail
decorator, the parser breaks and the whole thing crashes with an IndexError
. That is not supposed to happen because the parser was designed to throw custom errors when there is a problem. So, you either throw your own errors or generic errors, not both (imho). Since it starts to throw generic errors all over sudden means there is likely some corner case that was not considered.
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Ok, I have addressed:
For you to decide:
Also, when answering your questions, I misclicked the "Start Review" button (or something) and my last three comments ended up as a separate review. I cannot "dismiss" it, so if you could that would be great. Also, I am new to the review mechanic on github so please tell me when I am doing things wrong. Will be removing the |
I am having some problems with batavia so I decided to sidestep a little and just shuffle things around in
boolean.py
, will later be getting back to making batavia work.A thing to watch out for in this PR: readme display on
pypi
website might break because they use a rather strict and custom (afaik) parser for.rst
and.md
there. Once this lands, I will keep an eye on that and fix it if need be.