├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── dbx_unittest2pytest ├── __init__.py ├── fixes │ ├── __init__.py │ └── fix_asserts.py └── main.py ├── setup.py ├── tests ├── __init__.py └── test_fix_asserts.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | 64 | #vim files 65 | .*.swp 66 | .*.swo 67 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at travis-ci.org 2 | language: python 3 | 4 | matrix: 5 | include: 6 | - python: 2.7 7 | env: TOX_ENV=py27 8 | - python: 3.5 9 | env: TOX_ENV=py35 10 | 11 | script: 12 | - tox -e $TOX_ENV 13 | - flake8 14 | install: pip install flake8 tox 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Dropbox, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dbx-unittest2pytest [![Build Status](https://travis-ci.org/dropbox/dbx-unittest2pytest.svg?branch=master)](https://travis-ci.org/dropbox/dbx-unittest2pytest) 2 | 3 | ## Warning: this is not the official unittest2pytest! 4 | 5 | Dropbox developed this package in parallel with (what became) the official `pytest-dev` package `unittest2pytest`, that 6 | has the same name and does the same thing. It's the one that has the name `unittest2pytest` on PyPI, and it can convert 7 | a few more assertions. Check it out [on Github](https://github.com/pytest-dev/unittest2pytest) or 8 | [on PyPI](https://pypi.python.org/pypi/unittest2pytest). We've since renamed the project dbx-unittest2pytest to avoid confusion 9 | 10 | # Description 11 | 12 | Convert unittest asserts to pytest rewritten asserts. 13 | 14 | py.test supports advanced assertion introspection, allowing it to provide more detailed error messages. 15 | https://pytest.org/latest/assert.html#advanced-assertion-introspection 16 | 17 | Check out this blog post detailing how it works. 18 | http://pybites.blogspot.ie/2011/07/behind-scenes-of-pytests-new-assertion.html 19 | 20 | tl;dr 21 | If you are using py.test, then "assert a == b" is better than "self.assertEqual(a, b)" 22 | 23 | # What's the advantage? 24 | 25 | Pytest output before: 26 | ``` 27 | test/test_login.py:80: in test 28 | self.assertEquals(login.call_count, 1) 29 | E AssertionError: 0 != 1 30 | assert login.call_count == 1 31 | ``` 32 | Pytest output after: 33 | ``` 34 | test/test_login.py:80: in test 35 | E AssertionError: assert 0 == 1 36 | E + where 0 = .call_count 37 | ``` 38 | 39 | # What happens to my test code? 40 | 41 | Before: 42 | ```python 43 | self.assertEqual(a, b) 44 | self.assertEqual(a, None) 45 | self.assertFalse(a) 46 | ``` 47 | After: 48 | ```python 49 | assert a == b 50 | assert a is None 51 | assert not a 52 | ``` 53 | 54 | See unit tests for many more examples. 55 | 56 | # Usage 57 | ``` 58 | dbx-unittest2pytest --help 59 | dbx-unittest2pytest --fix=asserts 60 | ``` 61 | Run 4x parallel. 62 | ``` 63 | dbx-unittest2pytest --fix=asserts -j4 [filename/dirnames] 64 | ``` 65 | Write back to original files. 66 | ``` 67 | dbx-unittest2pytest --fix=asserts -w [filename/dirnames] 68 | ``` 69 | 70 | # Contributing 71 | Contributions are welcome. Tests can be run with [tox][tox]. Lint with [flake8][flake8] 72 | You'll have to agree to Dropbox's [CLA][CLA]. 73 | 74 | # Issues 75 | If you encounter any problems, please [file an issue][issues] along with a detailed description. 76 | 77 | [flake8]: https://flake8.readthedocs.org/en/latest/ 78 | [issues]: https://github.com/dropbox/dbx-unittest2pytest/issues 79 | [tox]: https://tox.readthedocs.org/en/latest/ 80 | [CLA]: https://opensource.dropbox.com/cla/ 81 | -------------------------------------------------------------------------------- /dbx_unittest2pytest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-unittest2pytest/dd4c596d2cd81ef1458a27cc0cf339bb9a57bd39/dbx_unittest2pytest/__init__.py -------------------------------------------------------------------------------- /dbx_unittest2pytest/fixes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-unittest2pytest/dd4c596d2cd81ef1458a27cc0cf339bb9a57bd39/dbx_unittest2pytest/fixes/__init__.py -------------------------------------------------------------------------------- /dbx_unittest2pytest/fixes/fix_asserts.py: -------------------------------------------------------------------------------- 1 | """Fixer that tranlslates unittest assertions to python assertions. 2 | https://github.com/dropbox/dbx-unittest2pytest 3 | 4 | E.g. 5 | self.assert_(x) -> assert x 6 | self.assertEquals(x, y) -> assert x == y 7 | 8 | """ 9 | from __future__ import absolute_import 10 | 11 | from lib2to3.fixer_base import BaseFix 12 | from lib2to3.fixer_util import Name, Comma, LParen, RParen 13 | from lib2to3.patcomp import PatternCompiler 14 | from lib2to3.pgen2 import token 15 | from lib2to3.pytree import Leaf, Node 16 | from lib2to3.pygram import python_symbols as syms 17 | 18 | import copy 19 | 20 | NOT = [Leaf(token.NAME, "not", prefix=" ")] 21 | EQUALS = [Leaf(token.EQEQUAL, "==", prefix=" ")] 22 | NOTEQUALS = [Leaf(token.NOTEQUAL, "!=", prefix=" ")] 23 | IN = [Leaf(token.NAME, "in", prefix=" ")] 24 | NOT_IN = NOT + IN 25 | IS = [Leaf(token.NAME, "is", prefix=" ")] 26 | IS_NOT = IS + NOT 27 | NONE = Leaf(token.NAME, "None", prefix=" ") 28 | 29 | GREATER = [Leaf(token.GREATER, ">", prefix=" ")] 30 | GREATER_EQUAL = [Leaf(token.GREATEREQUAL, ">=", prefix=" ")] 31 | LESS = [Leaf(token.LESS, "<", prefix=" ")] 32 | LESS_EQUAL = [Leaf(token.LESSEQUAL, "<=", prefix=" ")] 33 | 34 | TRUE = Name("True") 35 | FALSE = Name("False") 36 | 37 | PC = PatternCompiler() 38 | IN_PATTERN = PC.compile_pattern("comparison< a=any 'in' b=any >") 39 | NOTIN_PATTERN = PC.compile_pattern("comparison< a=any comp_op<'not' 'in'> b=any >") 40 | NUMBER_PATTERN = PC.compile_pattern("NUMBER | factor< ('+' | '-') NUMBER >") 41 | NOPAREN_PATTERN = PC.compile_pattern("power | atom") 42 | 43 | def make_operand(node): 44 | """Convert a node into something we can put in a statement. 45 | 46 | Adds parentheses if needed. 47 | """ 48 | if isinstance(node, Leaf) or NOPAREN_PATTERN.match(node): 49 | # No parentheses required in simple stuff 50 | result = [node.clone()] 51 | else: 52 | # Parentheses required in complex statements (eg. assertEqual(x + y, 17)) 53 | result = [LParen(), node.clone(), RParen()] 54 | result[0].prefix = node.prefix 55 | result[1].prefix = "" 56 | return result 57 | 58 | def make_assert_msg(msg): 59 | """Returns the assert message that should be added to the assertion. 60 | 61 | Args: 62 | msg: The Node holding the msg. (Could be a bare string or a keyword arg) 63 | """ 64 | if msg is None: 65 | return [] 66 | 67 | if msg.type == syms.argument: 68 | # If we have a `msg="blah"`, just return the "blah" portion but keep 69 | # the prefix of the `msg=` part. 70 | p_prefix = msg.prefix 71 | msg = msg.children[2] 72 | msg.prefix = p_prefix 73 | 74 | if not msg.prefix: 75 | msg.prefix = u" " 76 | if "\n" in msg.prefix: 77 | msg.prefix = " \\" + msg.prefix 78 | return [Comma()] + make_operand(msg) 79 | 80 | def assert_comparison(lhs, comparator, rhs, msg, prefix): 81 | """Build an assert statement in the AST""" 82 | children = [Name("assert")] 83 | 84 | # Single space after assert. Maintain the indentation/newline of the rhs, but 85 | # have to prepend a backslash for the assert to work. 86 | lhs.prefix = " " 87 | 88 | if "\n" in rhs.prefix: 89 | rhs.prefix = " \\" + rhs.prefix 90 | 91 | children.extend(make_operand(lhs)) 92 | children.extend(copy.deepcopy(comparator)) 93 | children.extend(make_operand(rhs)) 94 | children.extend(make_assert_msg(msg)) 95 | 96 | return Node(syms.assert_stmt, children, prefix=prefix) 97 | 98 | def assertion(expr, msg, prefix, is_not=False): 99 | """Build an assert statement in the AST""" 100 | children = [Name("assert")] 101 | if is_not: 102 | children.append(Leaf(token.NAME, "not", prefix=" ")) 103 | 104 | # Single space after assert. Maintain the indentation/newline of the expr. 105 | if expr.prefix == "": 106 | expr.prefix = " " 107 | for sub_node in expr.children: 108 | if '\n' in sub_node.prefix: 109 | sub_node.prefix = " \\" + sub_node.prefix 110 | children.append(expr.clone()) 111 | children.extend(make_assert_msg(msg)) 112 | 113 | return Node(syms.assert_stmt, children, prefix=prefix) 114 | 115 | class FixAsserts(BaseFix): 116 | """A Fixer in the lib2to3 framework for converting self.assertEqual or self.assertEquals 117 | to a regular assert statement. We do this because py.test can inspect types of statements 118 | if they use regular assert statements (better than the unit test framework can figure out) 119 | 120 | This now also converts a whole bunch of other TestCase assertions! 121 | """ 122 | METHODS = """ 123 | method=('assertEqual'|'assertEquals' 124 | |'assertNotEqual'|'assertNotEquals' 125 | |'assertTrue'|'assert_' 126 | |'assertFalse' 127 | |'assertIn'|'assertNotIn' 128 | |'assertIs'|'assertIsNot' 129 | |'assertIsNone'|'assertIsNotNone' 130 | |'assertGreater'|'assertGreaterEqual' 131 | |'assertLess'|'assertLessEqual' 132 | ) 133 | """ 134 | ONE_ARG = "(one=any)" 135 | ONE_ARG_COMMA = "arglist< one=any ',' >" 136 | TWO_ARG = "arglist< one=any ',' two=any [','] >" 137 | THREE_ARG = "arglist< one=any ',' two=any ',' three=any [','] >" 138 | PATTERN = """ 139 | power< 140 | 'self' 141 | trailer< '.' %(methods)s > 142 | trailer< '(' ( 143 | %(three_arg)s | 144 | %(two_arg)s | 145 | %(one_arg_comma)s | 146 | %(one_arg)s 147 | ) ')' > 148 | > 149 | """ % dict( 150 | methods=METHODS, 151 | one_arg=ONE_ARG, 152 | one_arg_comma=ONE_ARG_COMMA, 153 | two_arg=TWO_ARG, 154 | three_arg=THREE_ARG, 155 | ) 156 | 157 | def __init__(self, options, log): 158 | super(FixAsserts, self).__init__(options, log) 159 | 160 | def transform(self, node, results): 161 | """Returns what the above should be replaced with. 162 | 163 | This is called by the fixer. Whatever this returns will be replaced 164 | into the node. 165 | 166 | """ 167 | method = results['method'][0].value 168 | 169 | if "\n" in str(node) and "#" in str(node): 170 | # Bail on inline comments. We don't know what to do. 171 | return None 172 | 173 | # Handle all the one-arg cases 174 | 175 | if method in ('assertTrue', 'assert_'): 176 | # Replace with a simple assert. 177 | return assertion(results['one'], results.get('two'), prefix=node.prefix) 178 | if method == 'assertFalse': 179 | in_results = {} 180 | # Handle "self.assertFalse(a in b)" -> "assert a not in b" 181 | if IN_PATTERN.match(results['one'], in_results): 182 | return assert_comparison(in_results['a'], NOT_IN, in_results['b'], 183 | results.get('two'), prefix=node.prefix) 184 | # Handle "self.assertFalse(a not in b)" -> "assert a in b" 185 | if NOTIN_PATTERN.match(results['one'], in_results): 186 | return assert_comparison(in_results['a'], IN, in_results['b'], 187 | results.get('two'), prefix=node.prefix) 188 | 189 | # Replace with an assert but adding a 'not' in the front. 190 | return assertion(results['one'], results.get('two'), prefix=node.prefix, 191 | is_not=True) 192 | 193 | if method == 'assertIsNone': 194 | return assert_comparison(results['one'], IS, NONE, 195 | results.get('two'), prefix=node.prefix) 196 | if method == 'assertIsNotNone': 197 | return assert_comparison(results['one'], IS_NOT, NONE, 198 | results.get('two'), prefix=node.prefix) 199 | 200 | # Now handle the 2 arg cases 201 | 202 | if method in ('assertEqual', 'assertEquals'): 203 | # There are a couple of cases here. 204 | # If either side is a True or False we replace it with a simple assert. 205 | # i.e. "assert lhs" or "assert not lhs" 206 | # If either side is None we use "assert lhs|rhs is None" 207 | # Otherwise we do "assert lhs == rhs" 208 | lhs = results['one'] 209 | rhs = results['two'] 210 | 211 | if lhs in (TRUE, FALSE): 212 | rhs.prefix = lhs.prefix 213 | return assertion(rhs, results.get('three'), 214 | prefix=node.prefix, is_not=(lhs == FALSE)) 215 | 216 | if rhs in (TRUE, FALSE): 217 | return assertion(lhs, results.get('three'), 218 | prefix=node.prefix, is_not=(rhs == FALSE)) 219 | 220 | if NONE in (lhs, rhs): 221 | method = 'assertIs' 222 | 223 | # Fall through to assert_comparison logic 224 | 225 | if NUMBER_PATTERN.match(results['one']) or NUMBER_PATTERN.match(results['two']): 226 | # Don't use "is" comparison to integers. 227 | # It only works by accident in cpython for small integers (-6 <= x <= 255) 228 | # CPython caches "small" integers for performance. 229 | if method == 'assertIs': 230 | method = 'assertEqual' 231 | elif method == 'assertIsNot': 232 | method = 'assertNotEqual' 233 | 234 | comp_map = { 235 | 'assertEqual': EQUALS, 236 | 'assertEquals': EQUALS, 237 | 'assertNotEqual': NOTEQUALS, 238 | 'assertNotEquals': NOTEQUALS, 239 | 'assertGreater': GREATER, 240 | 'assertGreaterEqual': GREATER_EQUAL, 241 | 'assertLess': LESS, 242 | 'assertLessEqual': LESS_EQUAL, 243 | 'assertIn': IN, 244 | 'assertNotIn': NOT_IN, 245 | 'assertIs': IS, 246 | 'assertIsNot': IS_NOT, 247 | } 248 | 249 | assert method in comp_map 250 | 251 | return assert_comparison(results['one'], comp_map[method], results['two'], 252 | results.get('three'), prefix=node.prefix) 253 | -------------------------------------------------------------------------------- /dbx_unittest2pytest/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from lib2to3.main import main as lib_main 4 | 5 | def main(): 6 | sys.exit(lib_main("dbx_unittest2pytest.fixes")) 7 | 8 | 9 | if __name__ == '__main__': 10 | main() 11 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | setup( 4 | name="dbx-unittest2pytest", 5 | version="1.0", 6 | description='Convert unittest asserts to pytest rewritten asserts', 7 | keywords='unittest pytest dropbox', 8 | license='Apache License 2.0', 9 | author='Nipunn Koorapati, David Euresti', 10 | author_email='nipunn@dropbox.com, david@dropbox.com', 11 | packages=find_packages(exclude=['tests']), 12 | url='https://github.com/dropbox/dbx-unittest2pytest', 13 | zip_safe=False, 14 | 15 | entry_points={ 16 | 'console_scripts': ['dbx-unittest2pytest=dbx_unittest2pytest.main:main'], 17 | }, 18 | 19 | install_requires=[], 20 | ) 21 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-unittest2pytest/dd4c596d2cd81ef1458a27cc0cf339bb9a57bd39/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_fix_asserts.py: -------------------------------------------------------------------------------- 1 | from lib2to3.tests.test_fixers import FixerTestCase 2 | 3 | class TestFixAssertEqual(FixerTestCase): 4 | def setUp(self): 5 | super(TestFixAssertEqual, self).setUp( 6 | fix_list=["asserts"], 7 | fixer_pkg="dbx_unittest2pytest", 8 | ) 9 | 10 | def test_assert_equal_basic(self): 11 | self.check("self.assertEqual(55, 66)", "assert 55 == 66") 12 | self.check("self.assertEquals(55, 66)", "assert 55 == 66") 13 | self.check("self.assertEquals(55, 66,)", "assert 55 == 66") 14 | 15 | def test_assert_not_equal_basic(self): 16 | self.check("self.assertNotEqual(55, 66)", "assert 55 != 66") 17 | self.check("self.assertNotEquals(55, 66)", "assert 55 != 66") 18 | 19 | def test_assert_equal_msg(self): 20 | self.check("self.assertEqual(55, 66, 'Message')", "assert 55 == 66, 'Message'") 21 | self.check("self.assertEqual(55, 66, 'Message',)", "assert 55 == 66, 'Message'") 22 | 23 | def test_assert_equal_msg_kwd(self): 24 | self.check("self.assertEqual(55, 66, msg='Message')", "assert 55 == 66, 'Message'") 25 | 26 | def test_assert_true_false(self): 27 | self.check("self.assert_(obj)", "assert obj") 28 | self.check("self.assertTrue(obj)", "assert obj") 29 | self.check("self.assertTrue(obj,)", "assert obj") 30 | self.check("self.assertFalse(obj)", "assert not obj") 31 | 32 | def test_assert_false_in(self): 33 | self.check("self.assertFalse(a in b)", "assert a not in b") 34 | self.check("self.assertFalse(a in b,)", "assert a not in b") 35 | self.check("self.assertFalse(a in b, 'Message')", "assert a not in b, 'Message'") 36 | self.check("self.assertFalse(a in b, 'Message',)", "assert a not in b, 'Message'") 37 | self.check("self.assertFalse(a not in b)", "assert a in b") 38 | self.check("self.assertFalse(a not in b,)", "assert a in b") 39 | self.check("self.assertFalse(a not in b, 'Message')", "assert a in b, 'Message'") 40 | self.check("self.assertFalse(a not in b, 'Message',)", "assert a in b, 'Message'") 41 | 42 | def test_assert_equal_true_false(self): 43 | self.check("self.assertEquals(obj, True)", "assert obj") 44 | self.check("self.assertEqual(obj, False)", "assert not obj") 45 | self.check("self.assertEquals(True, obj)", "assert obj") 46 | self.check("self.assertEqual(False, obj)", "assert not obj") 47 | 48 | def test_assert_equal_none(self): 49 | self.check("self.assertEqual(None, obj)", "assert None is obj") 50 | self.check("self.assertEquals(obj, None)", "assert obj is None") 51 | 52 | def test_assert_is_none(self): 53 | self.check("self.assertIsNone(obj)", "assert obj is None") 54 | 55 | def test_assert_is_not_none(self): 56 | self.check("self.assertIsNotNone(obj)", "assert obj is not None") 57 | 58 | def test_assert_is_integer(self): 59 | # Don't use "is" comparison to integers. 60 | # It only works by accident in cpython for small integers (-6 <= x <= 255) 61 | # CPython caches "small" integers for performance. 62 | self.check("self.assertIs(obj, 22)", "assert obj == 22") 63 | self.check("self.assertIs(obj, +22)", "assert obj == (+22)") 64 | self.check("self.assertIs(obj, -10)", "assert obj == (-10)") 65 | self.check("self.assertIsNot(obj, -10)", "assert obj != (-10)") 66 | self.check("self.assertIs(obj, 'str')", "assert obj is 'str'") 67 | 68 | def test_comparisons(self): 69 | comp_map = { 70 | 'assertGreater': '>', 71 | 'assertGreaterEqual': '>=', 72 | 'assertLess': '<', 73 | 'assertLessEqual': '<=', 74 | 'assertIn': 'in', 75 | 'assertNotIn': 'not in', 76 | 'assertIs': 'is', 77 | 'assertIsNot': 'is not', 78 | } 79 | 80 | for method, comp in comp_map.items(): 81 | self.check("self.%s(obj1, obj2)" % method, "assert obj1 %s obj2" % comp) 82 | 83 | def test_assert_equal_multiline(self): 84 | b = """self.assertEqual( 85 | True == False, 86 | False == True, 87 | "Multiline" 88 | ) 89 | """ 90 | a = """assert (True == False) == \\ 91 | (False == True), \\ 92 | "Multiline" 93 | """ 94 | self.check(b, a) 95 | 96 | def test_assert_true_multiline(self): 97 | b = """self.assertTrue(a or 98 | b) 99 | """ 100 | a = """assert a or \\ 101 | b 102 | """ 103 | self.check(b, a) 104 | 105 | def test_assert_true_multiline_empty_first(self): 106 | b = """self.assertTrue( 107 | a or b 108 | ) 109 | """ 110 | a = """assert \\ 111 | a or b 112 | """ 113 | self.check(b, a) 114 | 115 | def test_assert_equal_multiline_trailing_comma(self): 116 | b = """self.assertEqual( 117 | True == False, 118 | False == True, 119 | "Multiline", 120 | ) 121 | """ 122 | a = """assert (True == False) == \\ 123 | (False == True), \\ 124 | "Multiline" 125 | """ 126 | self.check(b, a) 127 | 128 | def test_assert_equal_parenthesize(self): 129 | b = """self.assertEquals( 130 | 5 in [1, 2, 3], 131 | 6 in [4, 5, 6], 132 | "Parenthesize", 133 | ) 134 | """ 135 | a = """assert (5 in [1, 2, 3]) == \\ 136 | (6 in [4, 5, 6]), \\ 137 | "Parenthesize" 138 | """ 139 | self.check(b, a) 140 | 141 | def test_dont_parenthesize_array(self): 142 | b = "self.assertEquals('DontParenthesize', [1, 2, 3, 4])" 143 | a = "assert 'DontParenthesize' == [1, 2, 3, 4]" 144 | self.check(b, a) 145 | 146 | def test_assert_equal_multiline_comment(self): 147 | # We don't have a good way of handling this case, so bail instead 148 | a = """self.assertEqual( 149 | # Inline comment 150 | True == False, 151 | False == True, 152 | "Multiline" 153 | ) 154 | """ 155 | self.unchanged(a) 156 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # For more information about tox, see https://tox.readthedocs.org/en/latest/ 2 | [tox] 3 | envlist = py27,py35 4 | [testenv] 5 | deps= 6 | flake8 7 | mock 8 | pytest 9 | commands= 10 | flake8 11 | py.test 12 | [flake8] 13 | ignore = E302 14 | max-line-length = 100 15 | --------------------------------------------------------------------------------