Исключение нулевого указателя - это индикатор того, что вы используете объект, не инициализируя его.
Например, ниже - класс ученика, который будет использовать его в нашем коде.
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 ");
}
}
}
Я мог бы воспроизвести ошибку с минимальным примером:
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
с исключением в качестве побочного эффекта.
Для тех из нас, кому нужно смоделировать исключение и не может сделать это простым исправлением 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
. Ухоженная!
Я просто столкнулся с той же проблемой, когда издевался 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, но он, безусловно, проще, так как мне не нужно добавлять импорт в мои тесты и все равно работать так же. Мне кажется, это логичное завершение общей цепочки рассуждений в ответах здесь.