通过__init__构造对象并忽略构造函数异常

时间:2015-05-14 13:40:07

标签: python unit-testing

我有一个Python类,其__init__方法引发了一个名为WrongFileSpecified的自定义异常。

但是,当我编写单元测试时,我想从测试夹具中分配实例对象的属性。所以通常我要做的是从文件中读取数据然后使用实例对象。

但是通过测试,我无法使用任何测试文件,因此我基本上需要在单元测试的setUp方法中对实例对象中的数据进行硬编码。有没有办法在没有__init__抱怨异常的情况下创建实例?

示例代码:

class A(object):
    def __init__(self, folderPath):
        #check folder path using os.isdir() otherwise raise exception
        #...
        self.folderPath = folderPath
        #Call load record
        self._load_records() #uses self.folderPath and raises exceptions as well

        #Note i cannot avoid raising these exceptions, its required

class TestA(unittest.TestCase):
   .......
   obj = None
   def setUp(self):
        obj = A('fake folder path')
        obj.val1 = "testparam1"
        obj.param2 = "testparam2"

   def test_1(self):
..... 

3 个答案:

答案 0 :(得分:1)

您可以使用var outputReport = ''; var uidList = []; $(function() { var listSource = { datatype: "json", dataFields : [ { name : "First", type : 'string' }, { name : "Last", type : 'string' } ], id: 'id', localdata: [{'id': 1, 'First': 'Abhishek', 'Last': 'Shringi'},{'id': 2, 'First': 'Abhishek', 'Last': 'Shringi'},{'id': 3, 'First': 'Abhishek', 'Last': 'Shringi'},{'id': 4, 'First': 'Abhishek', 'Last': 'Shringi'},{'id': 5, 'First': 'Abhishek', 'Last': 'Shringi'},{'id': 6, 'First': 'Abhishek', 'Last': 'Shringi'}] }; var listAdapter = new $.jqx.dataAdapter(listSource); //Dropdown source for PW Reset var dropDownListSource = { datatype: "json", datafields: [ { name: 'name' } ], id: 'id', localdata: [{'id': 1, 'name': 'New'},{'id': 2, 'name': 'Yes'},{'id': 3, 'name': 'No'}] }; var dropdownListAdapter = new $.jqx.dataAdapter(dropDownListSource, { autoBind: true, async: false }); var dropdownListSource = []; for (var i = 0; i < dropdownListAdapter.records.length; i++) { dropdownListSource[i] = dropdownListAdapter.records[i]['name']; } // create list user grid $('#list-users-table').jqxGrid({ source : listAdapter, altrows: true, autoheight: true, width: '99%', columnsheight: 30, editable: true, editmode: 'click', pagesizeOptions: ['2','4','6','8'], pagesize: 2, pageable: true, filterable: true, sortable: true, virtualmode: true, selectionmode: 'multiplerowsextended', filtermode: 'default', rendergridrows: function(params) { return params.data; }, handlekeyboardnavigation: function(e) { var key = event.charCode ? event.charCode : event.keyCode ? event.keyCode : 0; if (key == 39) { $("#list-users-table").jqxGrid('gotonextpage'); } else if (key == 37) { $("#list-users-table").jqxGrid('gotoprevpage'); } e.stopPropagation(); e.event.cancelbubble = true; }, columnsresize: true, showheader: true, columns : [ { text : 'PW Reset', datafield : 'Reset', width : 80, columntype: 'dropdownlist', initeditor: function (row, cellvalue, editor) { editor.jqxDropDownList({ source: dropdownListSource}); editor.on('select', function(event) { var args = event.args; if (args) { var index = args.index; var item = args.item; var resetType = item.label; var value = item.value; var selection = $("#list-users-table").jqxGrid('getselectedrowindexes'); if (selection && selection.length > 0) { for (var i = 0; i < selection.length; i++) { var rowData = $('#list-users-table').jqxGrid('getrowdata', selection[i]); } } } }); } }, { text : 'First Name', datafield : 'First', width : 120, editable : false, align: 'center' }, { text : 'Last Name', datafield : 'Last', width : 120, editable : false, align: 'center' } ] }); //function calls when apply filter $('#list-users-table').on('filter', function (event) { if(event.args.filters.length > 0) { $("#export-user-menu-item").show(); } else { $("#export-user-menu-item").hide(); } $('#list-users-table').jqxGrid('updatebounddata', 'filter'); $("#list-users-table").jqxGrid('clearselection'); }); //function calls when apply sorting $('#list-users-table').on('sort', function (event) { $('#list-users-table').jqxGrid('updatebounddata', 'sort'); $("#list-users-table").jqxGrid('clearselection'); }); }); 绕过__init__来创建一个空对象。

__new__

请注意,obj = obj_type.__new__(obj_type) 是适当的obj_type对象。这有点hacky但它​​的工作原理。您负责设置对象的成员。

编辑:这是一个例子。

type

控制台输出:

class Foo():
    def __init__(self):
        self.x = 1
        self.y = 2

    def say_hello(self):
        print('Hello!')

r = Foo.__new__(Foo)

r.say_hello()

print(r.x)

答案 1 :(得分:0)

以下是两个选项:

  1. 将文件加载到类方法,这是提供替代构造函数的Pythonic方法(见下文);或
  2. __init__提供一个额外参数,以便在必要时(例如def __init__(self, folderPath, suppress=False)validate=True禁止例外情况,无论哪种情况对您的使用更有意义)。
  3. 在我看来,后者有点尴尬,但这意味着您不必重构现有代码来创建A个实例。前者看起来像:

    class A(object):
    
        def __init__(self, ...):
            """Pass whatever is loaded from the file to __init__."""
            ...
    
        @classmethod
        def from_file(cls, folderPath):
            """Load the data from the file, or raise an exception."""
            ...
    

    你会替换例如a = A(whatever)a = A.from_file(whatever)

答案 2 :(得分:0)

有一个名为mock的非常有用的模块,你可以稍后查看,我觉得在这种情况下它会太多了。相反,您应该考虑重新设计您的课程,例如:

class A(object):

    def __init__(self, folderPath):
        self.folderPath = folderPath

    def _load_records(self)
        #check folder path using os.isdir() otherwise raise exception
        ...
        #uses self.folderPath and raises exceptions as well
        ...

    @classmethod
    def load_records(cls, folderpath):
        obj = cls(folderpath)
        obj._load_records()
        return obj

    # Usage
    records = A.load_records('/path/to/records')

然后你可以这样做:

class TestA(unittest.TestCase):
   .......
   obj = None
   def setUp(self):
        self.obj = A('fake folder path')
        self.obj.val1 = "testparam1"
        self.obj.param2 = "testparam2"

   def test_1(self):
        self.assertRaises(self.obj._load_records, HorribleFailureError)

此外我强烈建议您查看pytest,它有很棒的测试设施,包括文件夹和文件夹。