Не удается поймать смоделированное исключение, потому что оно не наследует BaseException

Исключение нулевого указателя - это индикатор того, что вы используете объект, не инициализируя его.

Например, ниже - класс ученика, который будет использовать его в нашем коде.

public class Student {

    private int id;

    public int getId() {
        return this.id;
    }

    public setId(int newId) {
        this.id = newId;
    }
}

Приведенный ниже код дает вам исключение с нулевым указателем.

public class School {

    Student obj_Student;

    public School() {
        try {
            obj_Student.getId();
        }
        catch(Exception e) {
            System.out.println("Null Pointer ");
        }
    }
}

Поскольку вы используете Obj_Student, но вы забыли инициализировать его, как в правильном коде, показанном ниже:

public class School {

    Student obj_Student;

    public School() {
        try {
            obj_Student = new Student();
            obj_Student.setId(12);
            obj_Student.getId();
        }
        catch(Exception e) {
            System.out.println("Null Pointer ");
        }
    }
}
25
задан Community 23 May 2017 в 12:02
поделиться

3 ответа

Я мог бы воспроизвести ошибку с минимальным примером:

foo.py:

class MyError(Exception):
    pass

class A:
    def inner(self):
        err = MyError("FOO")
        print(type(err))
        raise err
    def outer(self):
        try:
            self.inner()
        except MyError as err:
            print ("catched ", err)
        return "OK"

Тест без насмешек:

class FooTest(unittest.TestCase):
    def test_inner(self):
        a = foo.A()
        self.assertRaises(foo.MyError, a.inner)
    def test_outer(self):
        a = foo.A()
        self.assertEquals("OK", a.outer())

Хорошо, все в порядке , оба теста пройдены

Проблема приходит с макетами. Как только класс MyError подвергается насмешке, предложение expect ничего не может поймать, и я получаю ту же ошибку, что и пример из вопроса:

class FooTest(unittest.TestCase):
    def test_inner(self):
        a = foo.A()
        self.assertRaises(foo.MyError, a.inner)
    def test_outer(self):
        with unittest.mock.patch('foo.MyError'):
            a = exc2.A()
            self.assertEquals("OK", a.outer())

Немедленно дает:

ERROR: test_outer (__main__.FooTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "...\foo.py", line 11, in outer
    self.inner()
  File "...\foo.py", line 8, in inner
    raise err
TypeError: exceptions must derive from BaseException

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<pyshell#78>", line 8, in test_outer
  File "...\foo.py", line 12, in outer
    except MyError as err:
TypeError: catching classes that do not inherit from BaseException is not allowed

Здесь я получаю первый TypeError, которого у вас не было, потому что я поднимаю макет, когда вы вызвали истинное исключение с 'requests.exceptions.ConnectionError': requests.exceptions.ConnectionError в конфигурации. Но проблема остается в том, что предложение except пытается поймать насмешку .

TL / DR: когда вы макетируете полный пакет requests, предложение except requests.exceptions.ConnectionError пытается поймать макет. Поскольку макет на самом деле не BaseException, он вызывает ошибку.

Единственное решение, которое я могу себе представить, - не издеваться над полным requests, а только частями, которые не являются исключениями. Должен признать, что я не мог найти, как сказать, чтобы издеваться над издевались над всем, кроме этого , но в вашем примере вам нужно только исправить requests.head. Поэтому я думаю, что это должно работать:

def test_bad_connection(self):
    with mock.patch('path.to.my.package.requests.head',
                    side_effect=requests.exceptions.ConnectionError):
        self.assertEqual(
            mypackage.myMethod('some_address',
            mypackage.successfulConnection.FAILURE
        )

То есть: исправлять только метод head с исключением в качестве побочного эффекта.

27
ответ дан Serge Ballesta 23 May 2017 в 12:02
поделиться

Для тех из нас, кому нужно смоделировать исключение и не может сделать это простым исправлением head, вот простое решение, которое заменяет целевое исключение пустым:

Скажем, у нас есть универсальный модуль для тестирования с исключением, который мы должны смоделировать:

# app/foo_file.py
def test_me():
    try:
       foo()
       return "No foo error happened"
    except CustomError:  # <-- Mock me!
        return "The foo error was caught"

Мы хотим смоделировать CustomError, но поскольку это исключение, мы сталкиваемся с проблемами, если пытаемся исправить его, как и все остальное. Обычно вызов patch заменяет цель на MagicMock, но здесь это не сработает. Насмешки изящны, но они ведут себя не так, как исключения. Вместо того, чтобы патчить с макетом, давайте вместо этого сделаем исключение для заглушки. Мы сделаем это в нашем тестовом файле.

# app/test_foo_file.py
from mock import patch


# A do-nothing exception we are going to replace CustomError with
class StubException(Exception):
    pass


# Now apply it to our test
@patch('app.foo_file.foo')
@patch('app.foo_file.CustomError', new_callable=lambda: StubException)
def test_foo(stub_exception, mock_foo):
    mock_foo.side_effect = stub_exception("Stub")  # Raise our stub to be caught by CustomError
    assert test_me() == "The error was caught"

# Success!

Так что с lambda? Параметр new_callable вызывает все, что мы ему даем, и заменяет цель возвращением этого вызова. Если мы передадим наш класс StubException прямо, он вызовет конструктор класса и исправит наш целевой объект с исключением instance , а не class , что не то, что мы хотим. Обернув его в lambda, он возвращает наш класс, как мы и собирались.

Как только наше исправление выполнено, объект stub_exception (который буквально является нашим классом StubException) может быть поднят и пойман, как если бы это был CustomError. Ухоженная!

1
ответ дан user2859458 23 May 2017 в 12:02
поделиться

Я просто столкнулся с той же проблемой, когда издевался struct .

Я получаю сообщение об ошибке:

TypeError: перехват классов, которые не наследуются от BaseException, не допускается

При попытке отловить struct.error, поднятый из struct.unpack.

Я обнаружил, что самым простым способом обойти это в моих тестах было просто установить значение атрибута ошибки в моем макете равным Exception. Например,

Метод, который я хочу проверить, имеет следующий базовый шаблон:

def some_meth(self):
    try:
        struct.unpack(fmt, data)
    except struct.error:
        return False
    return True

Тест имеет этот базовый шаблон.

@mock.patch('my_module.struct')
def test_some_meth(self, struct_mock):
    '''Explain how some_func should work.'''
    struct_mock.error = Exception
    self.my_object.some_meth()
    struct_mock.unpack.assert_called()
    struct_mock.unpack.side_effect = struct_mock.error
    self.assertFalse(self.my_object.some_meth()

Это похоже на подход, принятый @BillB, но он, безусловно, проще, так как мне не нужно добавлять импорт в мои тесты и все равно работать так же. Мне кажется, это логичное завершение общей цепочки рассуждений в ответах здесь.

0
ответ дан Grr 23 May 2017 в 12:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: