Hello community, here is the log from the commit of package python-ddt for openSUSE:Factory checked in at 2020-03-27 00:24:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-ddt (Old) and /work/SRC/openSUSE:Factory/.python-ddt.new.3160 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ddt" Fri Mar 27 00:24:14 2020 rev:9 rq:785070 version:1.3.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-ddt/python-ddt.changes 2020-01-24 14:21:17.506622032 +0100 +++ /work/SRC/openSUSE:Factory/.python-ddt.new.3160/python-ddt.changes 2020-03-27 00:24:15.864225408 +0100 @@ -1,0 +2,7 @@ +Sat Mar 14 15:49:10 UTC 2020 - Dirk Mueller <dmuel...@suse.com> + +- update to 1.3.0: + * Added the ability to specify the YAML loader in the file_data decorator (#77) + * Dropped Python 3.4 support + +------------------------------------------------------------------- Old: ---- ddt-1.2.2.tar.gz New: ---- ddt-1.3.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-ddt.spec ++++++ --- /var/tmp/diff_new_pack.fZbFb4/_old 2020-03-27 00:24:16.776225870 +0100 +++ /var/tmp/diff_new_pack.fZbFb4/_new 2020-03-27 00:24:16.780225872 +0100 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %bcond_without python2 Name: python-ddt -Version: 1.2.2 +Version: 1.3.0 Release: 0 Summary: Data-Driven/Decorated Tests License: MIT ++++++ ddt-1.2.2.tar.gz -> ddt-1.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/PKG-INFO new/ddt-1.3.0/PKG-INFO --- old/ddt-1.2.2/PKG-INFO 2019-12-02 07:22:06.000000000 +0100 +++ new/ddt-1.3.0/PKG-INFO 2020-03-10 03:37:11.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: ddt -Version: 1.2.2 +Version: 1.3.0 Summary: Data-Driven/Decorated Tests Home-page: https://github.com/datadriventests/ddt Author: Carles Barrobés @@ -16,6 +16,5 @@ Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Software Development :: Testing diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/ddt.egg-info/PKG-INFO new/ddt-1.3.0/ddt.egg-info/PKG-INFO --- old/ddt-1.2.2/ddt.egg-info/PKG-INFO 2019-12-02 07:22:06.000000000 +0100 +++ new/ddt-1.3.0/ddt.egg-info/PKG-INFO 2020-03-10 03:37:11.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: ddt -Version: 1.2.2 +Version: 1.3.0 Summary: Data-Driven/Decorated Tests Home-page: https://github.com/datadriventests/ddt Author: Carles Barrobés @@ -16,6 +16,5 @@ Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Software Development :: Testing diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/ddt.egg-info/SOURCES.txt new/ddt-1.3.0/ddt.egg-info/SOURCES.txt --- old/ddt-1.2.2/ddt.egg-info/SOURCES.txt 2019-12-02 07:22:06.000000000 +0100 +++ new/ddt-1.3.0/ddt.egg-info/SOURCES.txt 2020-03-10 03:37:11.000000000 +0100 @@ -11,11 +11,13 @@ ddt.egg-info/top_level.txt test/__init__.py test/mycode.py -test/test_data_dict.json -test/test_data_dict.yaml -test/test_data_dict_dict.json -test/test_data_dict_dict.yaml -test/test_data_list.json -test/test_data_list.yaml test/test_example.py -test/test_functional.py \ No newline at end of file +test/test_functional.py +test/data/test_custom_yaml_loader.yaml +test/data/test_data_dict.json +test/data/test_data_dict.yaml +test/data/test_data_dict_dict.json +test/data/test_data_dict_dict.yaml +test/data/test_data_list.json +test/data/test_data_list.yaml +test/data/test_functional_custom_tags.yaml \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/ddt.py new/ddt-1.3.0/ddt.py --- old/ddt-1.2.2/ddt.py 2019-12-02 07:18:08.000000000 +0100 +++ new/ddt-1.3.0/ddt.py 2020-03-10 03:36:15.000000000 +0100 @@ -19,16 +19,17 @@ else: _have_yaml = True -__version__ = '1.2.2' +__version__ = '1.3.0' # These attributes will not conflict with any real python attribute # They are added to the decorated test method and processed later # by the `ddt` class decorator. -DATA_ATTR = '%values' # store the data the test must run with -FILE_ATTR = '%file_path' # store the path to JSON file -UNPACK_ATTR = '%unpack' # remember that we have to unpack values -index_len = 5 # default max length of case index +DATA_ATTR = '%values' # store the data the test must run with +FILE_ATTR = '%file_path' # store the path to JSON file +YAML_LOADER_ATTR = '%yaml_loader' # store custom yaml loader for serialization +UNPACK_ATTR = '%unpack' # remember that we have to unpack values +index_len = 5 # default max length of case index try: @@ -79,7 +80,7 @@ return wrapper -def file_data(value): +def file_data(value, yaml_loader=None): """ Method decorator to add to your test methods. @@ -97,9 +98,14 @@ In case of a dict, keys will be used as suffixes to the name of the test case, and values will be fed as test data. + ``yaml_loader`` can be used to customize yaml deserialization. + The default is ``None``, which results in using the ``yaml.safe_load`` + method. """ def wrapper(func): setattr(func, FILE_ATTR, value) + if yaml_loader: + setattr(func, YAML_LOADER_ATTR, yaml_loader) return func return wrapper @@ -212,7 +218,11 @@ with codecs.open(data_file_path, 'r', 'utf-8') as f: # Load the data from YAML or JSON if _is_yaml_file: - data = yaml.safe_load(f) + if hasattr(func, YAML_LOADER_ATTR): + yaml_loader = getattr(func, YAML_LOADER_ATTR) + data = yaml.load(f, Loader=yaml_loader) + else: + data = yaml.safe_load(f) else: data = json.load(f) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/setup.py new/ddt-1.3.0/setup.py --- old/ddt-1.2.2/setup.py 2019-12-02 07:11:37.000000000 +0100 +++ new/ddt-1.3.0/setup.py 2020-03-10 03:02:19.000000000 +0100 @@ -22,7 +22,6 @@ 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Topic :: Software Development :: Testing', ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/data/test_custom_yaml_loader.yaml new/ddt-1.3.0/test/data/test_custom_yaml_loader.yaml --- old/ddt-1.2.2/test/data/test_custom_yaml_loader.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/ddt-1.3.0/test/data/test_custom_yaml_loader.yaml 2020-03-10 03:03:24.000000000 +0100 @@ -0,0 +1,65 @@ +bool: + instance: !!bool "false" + expected: false + +str: + instance: !!str "test" + expected: test + +int: + instance: !!int "32" + expected: 32 + +float: + instance: !!float "3.123" + expected: 3.123 + +python_list: + instance: !!python/list [1,2,3,4] + expected: + - 1 + - 2 + - 3 + - 4 + +python_dict: + instance: !!python/dict + a: 1 + b: asd + c: false + expected: + a: 1 + b: asd + c: false + +my_class: + instance: !!python/object:test.test_example.MyClass + a: 132 + b: true + c: + - alpha + - beta + d: + _a: 1 + _b: test + expected: + a: 132 + b: true + c: + - alpha + - beta + d: + _a: 1 + _b: test + +python_str: + instance: !!python/str "test" + expected: test + +python_int: + instance: !!python/int "32" + expected: 32 + +python_float: + instance: !!python/float "3.123" + expected: 3.123 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/data/test_data_dict.json new/ddt-1.3.0/test/data/test_data_dict.json --- old/ddt-1.2.2/test/data/test_data_dict.json 1970-01-01 01:00:00.000000000 +0100 +++ new/ddt-1.3.0/test/data/test_data_dict.json 2020-03-10 03:03:24.000000000 +0100 @@ -0,0 +1,4 @@ +{ + "unsorted_list": [ 10, 12, 15 ], + "sorted_list": [ 15, 12, 50 ] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/data/test_data_dict.yaml new/ddt-1.3.0/test/data/test_data_dict.yaml --- old/ddt-1.2.2/test/data/test_data_dict.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/ddt-1.3.0/test/data/test_data_dict.yaml 2020-03-10 03:03:24.000000000 +0100 @@ -0,0 +1,6 @@ +unsorted_list: + - 10 + - 15 + - 12 + +sorted_list: [ 15, 12, 50 ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/data/test_data_dict_dict.json new/ddt-1.3.0/test/data/test_data_dict_dict.json --- old/ddt-1.2.2/test/data/test_data_dict_dict.json 1970-01-01 01:00:00.000000000 +0100 +++ new/ddt-1.3.0/test/data/test_data_dict_dict.json 2020-03-10 03:03:24.000000000 +0100 @@ -0,0 +1,22 @@ +{ + "positive_integer_range": { + "start": 0, + "end": 2, + "value": 1 + }, + "negative_integer_range": { + "start": -2, + "end": 0, + "value": -1 + }, + "positive_real_range": { + "start": 0.0, + "end": 1.0, + "value": 0.5 + }, + "negative_real_range": { + "start": -1.0, + "end": 0.0, + "value": -0.5 + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/data/test_data_dict_dict.yaml new/ddt-1.3.0/test/data/test_data_dict_dict.yaml --- old/ddt-1.2.2/test/data/test_data_dict_dict.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/ddt-1.3.0/test/data/test_data_dict_dict.yaml 2020-03-10 03:03:24.000000000 +0100 @@ -0,0 +1,19 @@ +positive_integer_range: + start: 0 + end: 2 + value: 1 + +negative_integer_range: + start: -2 + end: 0 + value: -1 + +positive_real_range: + start: 0.0 + end: 1.0 + value: 0.5 + +negative_real_range: + start: -1.0 + end: 0.0 + value: -0.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/data/test_data_list.json new/ddt-1.3.0/test/data/test_data_list.json --- old/ddt-1.2.2/test/data/test_data_list.json 1970-01-01 01:00:00.000000000 +0100 +++ new/ddt-1.3.0/test/data/test_data_list.json 2020-03-10 03:03:24.000000000 +0100 @@ -0,0 +1,5 @@ +[ + "Hello", + "Goodbye" +] + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/data/test_data_list.yaml new/ddt-1.3.0/test/data/test_data_list.yaml --- old/ddt-1.2.2/test/data/test_data_list.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/ddt-1.3.0/test/data/test_data_list.yaml 2020-03-10 03:03:24.000000000 +0100 @@ -0,0 +1,2 @@ +- "Hello" +- "Goodbye" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/data/test_functional_custom_tags.yaml new/ddt-1.3.0/test/data/test_functional_custom_tags.yaml --- old/ddt-1.2.2/test/data/test_functional_custom_tags.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/ddt-1.3.0/test/data/test_functional_custom_tags.yaml 2020-03-10 03:03:24.000000000 +0100 @@ -0,0 +1,3 @@ +custom_class: + instance: !!python/object:test.test_functional.CustomClass {} + expected: CustomClass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/test_data_dict.json new/ddt-1.3.0/test/test_data_dict.json --- old/ddt-1.2.2/test/test_data_dict.json 2019-07-23 23:00:08.000000000 +0200 +++ new/ddt-1.3.0/test/test_data_dict.json 1970-01-01 01:00:00.000000000 +0100 @@ -1,4 +0,0 @@ -{ - "unsorted_list": [ 10, 12, 15 ], - "sorted_list": [ 15, 12, 50 ] -} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/test_data_dict.yaml new/ddt-1.3.0/test/test_data_dict.yaml --- old/ddt-1.2.2/test/test_data_dict.yaml 2019-07-23 23:00:08.000000000 +0200 +++ new/ddt-1.3.0/test/test_data_dict.yaml 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +0,0 @@ -unsorted_list: - - 10 - - 15 - - 12 - -sorted_list: [ 15, 12, 50 ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/test_data_dict_dict.json new/ddt-1.3.0/test/test_data_dict_dict.json --- old/ddt-1.2.2/test/test_data_dict_dict.json 2019-07-23 23:00:08.000000000 +0200 +++ new/ddt-1.3.0/test/test_data_dict_dict.json 1970-01-01 01:00:00.000000000 +0100 @@ -1,22 +0,0 @@ -{ - "positive_integer_range": { - "start": 0, - "end": 2, - "value": 1 - }, - "negative_integer_range": { - "start": -2, - "end": 0, - "value": -1 - }, - "positive_real_range": { - "start": 0.0, - "end": 1.0, - "value": 0.5 - }, - "negative_real_range": { - "start": -1.0, - "end": 0.0, - "value": -0.5 - } -} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/test_data_dict_dict.yaml new/ddt-1.3.0/test/test_data_dict_dict.yaml --- old/ddt-1.2.2/test/test_data_dict_dict.yaml 2019-07-23 23:00:08.000000000 +0200 +++ new/ddt-1.3.0/test/test_data_dict_dict.yaml 1970-01-01 01:00:00.000000000 +0100 @@ -1,19 +0,0 @@ -positive_integer_range: - start: 0 - end: 2 - value: 1 - -negative_integer_range: - start: -2 - end: 0 - value: -1 - -positive_real_range: - start: 0.0 - end: 1.0 - value: 0.5 - -negative_real_range: - start: -1.0 - end: 0.0 - value: -0.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/test_data_list.json new/ddt-1.3.0/test/test_data_list.json --- old/ddt-1.2.2/test/test_data_list.json 2019-07-23 23:00:08.000000000 +0200 +++ new/ddt-1.3.0/test/test_data_list.json 1970-01-01 01:00:00.000000000 +0100 @@ -1,5 +0,0 @@ -[ - "Hello", - "Goodbye" -] - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/test_data_list.yaml new/ddt-1.3.0/test/test_data_list.yaml --- old/ddt-1.2.2/test/test_data_list.yaml 2019-07-23 23:00:08.000000000 +0200 +++ new/ddt-1.3.0/test/test_data_list.yaml 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -- "Hello" -- "Goodbye" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/test_example.py new/ddt-1.3.0/test/test_example.py --- old/ddt-1.2.2/test/test_example.py 2019-07-23 23:00:08.000000000 +0200 +++ new/ddt-1.3.0/test/test_example.py 2020-03-10 03:03:24.000000000 +0100 @@ -1,4 +1,5 @@ import unittest + from ddt import ddt, data, file_data, unpack from test.mycode import larger_than_two, has_three_elements, is_a_greeting @@ -8,7 +9,6 @@ have_yaml_support = False else: have_yaml_support = True - del yaml # A good-looking decorator needs_yaml = unittest.skipUnless( @@ -20,6 +20,19 @@ pass +class MyClass: + def __init__(self, **kwargs): + for field, value in kwargs.items(): + setattr(self, field, value) + + def __eq__(self, other): + return isinstance(other, dict) and vars(self) == other or \ + isinstance(other, MyClass) and vars(self) == vars(other) + + def __str__(self): + return "TestObject %s" % vars(self) + + def annotated(a, b): r = Mylist([a, b]) setattr(r, "__name__", "test_%d_greater_than_%d" % (a, b)) @@ -59,34 +72,34 @@ self.assertIsNotNone(getattr(value, "__name__")) self.assertIsNotNone(getattr(value, "__doc__")) - @file_data("test_data_dict_dict.json") + @file_data('data/test_data_dict_dict.json') def test_file_data_json_dict_dict(self, start, end, value): self.assertLess(start, end) self.assertLess(value, end) self.assertGreater(value, start) - @file_data('test_data_dict.json') + @file_data('data/test_data_dict.json') def test_file_data_json_dict(self, value): self.assertTrue(has_three_elements(value)) - @file_data('test_data_list.json') + @file_data('data/test_data_list.json') def test_file_data_json_list(self, value): self.assertTrue(is_a_greeting(value)) @needs_yaml - @file_data("test_data_dict_dict.yaml") + @file_data('data/test_data_dict_dict.yaml') def test_file_data_yaml_dict_dict(self, start, end, value): self.assertLess(start, end) self.assertLess(value, end) self.assertGreater(value, start) @needs_yaml - @file_data('test_data_dict.yaml') + @file_data('data/test_data_dict.yaml') def test_file_data_yaml_dict(self, value): self.assertTrue(has_three_elements(value)) @needs_yaml - @file_data('test_data_list.yaml') + @file_data('data/test_data_list.yaml') def test_file_data_yaml_list(self, value): self.assertTrue(is_a_greeting(value)) @@ -130,3 +143,15 @@ def test_list_extracted_with_doc(self, first_value, second_value): """Extract into args with first value {} and second value {}""" self.assertTrue(first_value > second_value) + + +if have_yaml_support: + # This test will only succeed if the execution context is from the ddt + # directory. pyyaml cannot locate test.test_example.MyClass otherwise! + + @ddt + class YamlOnlyTestCase(unittest.TestCase): + @file_data('data/test_custom_yaml_loader.yaml', yaml.FullLoader) + def test_custom_yaml_loader(self, instance, expected): + """Test with yaml tags to create specific classes to compare""" + self.assertEqual(expected, instance) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddt-1.2.2/test/test_functional.py new/ddt-1.3.0/test/test_functional.py --- old/ddt-1.2.2/test/test_functional.py 2019-12-02 07:11:37.000000000 +0100 +++ new/ddt-1.3.0/test/test_functional.py 2020-03-10 03:03:24.000000000 +0100 @@ -1,7 +1,9 @@ import os import json +from sys import modules import six + try: from unittest import mock except ImportError: @@ -15,6 +17,10 @@ from test.mycode import has_three_elements +class CustomClass: + pass + + @ddt class Dummy(object): """ @@ -44,7 +50,7 @@ Dummy class to test the file_data decorator on """ - @file_data("test_data_dict.json") + @file_data("data/test_data_dict.json") def test_something_again(self, value): return value @@ -56,7 +62,7 @@ JSON file is missing """ - @file_data("test_data_dict_missing.json") + @file_data("data/test_data_dict_missing.json") def test_something_again(self, value): return value @@ -68,7 +74,7 @@ YAML file is missing """ - @file_data("test_data_dict_missing.yaml") + @file_data("data/test_data_dict_missing.yaml") def test_something_again(self, value): return value @@ -148,7 +154,7 @@ tests = set(filter(_is_test, FileDataDummy.__dict__)) tests_dir = os.path.dirname(__file__) - test_data_path = os.path.join(tests_dir, 'test_data_dict.json') + test_data_path = os.path.join(tests_dir, 'data/test_data_dict.json') test_data = json.loads(open(test_data_path).read()) index_len = len(str(len(test_data))) created_tests = set([ @@ -376,7 +382,7 @@ @ddt class NoYAMLInstalledTest(object): - @file_data('test_data_dict.yaml') + @file_data('data/test_data_dict.yaml') def test_file_data_yaml_dict(self, value): assert_true(has_three_elements(value)) @@ -386,3 +392,42 @@ for test in tests: method = getattr(obj, test) assert_raises(ValueError, method) + + +def test_load_yaml_with_python_tag(): + """ + Test that YAML files containing python tags throw no exception if an + loader allowing python tags is passed. + """ + + from yaml import FullLoader + from yaml.constructor import ConstructorError + + def str_to_type(class_name): + return getattr(modules[__name__], class_name) + + try: + @ddt + class YamlDefaultLoaderTest(object): + @file_data('data/test_functional_custom_tags.yaml') + def test_cls_is_instance(self, cls, expected): + assert_true(isinstance(cls, str_to_type(expected))) + except Exception as e: + if not isinstance(e, ConstructorError): + raise AssertionError() + + @ddt + class YamlFullLoaderTest(object): + @file_data('data/test_functional_custom_tags.yaml', FullLoader) + def test_cls_is_instance(self, instance, expected): + assert_true(isinstance(instance, str_to_type(expected))) + + tests = list(filter(_is_test, YamlFullLoaderTest.__dict__)) + obj = YamlFullLoaderTest() + + if not tests: + raise AssertionError('No tests have been found.') + + for test in tests: + method = getattr(obj, test) + method()