英文:
Pytest unit testing getting a Can't instantiate abstract class error
问题
I'm currently testing this code using pytest from Storm_Centre.py
class Storm_Centre:
def __init__(self):
self.storm_list = []
def already_exists(self, name) -> bool:
for storm in self.storm_list:
if storm.name == name:
return True
return False
this code uses code from Storm.py
from abc import ABC, abstractmethod
class Storm(ABC):
def __init__(self, name, wind_speed):
self.name = name
self.wind_speed = wind_speed
super().__init__()
@abstractmethod
def calculate_classification(self):
pass
@abstractmethod
def get_advice(self):
pass
I'm testing the code in test_storm.py using the following code:
import pytest
from Storm import Storm
from Storm_Centre import Storm_Centre
def test_already_exists():
s1 = Storm('Jeff', 10)
c1 = Storm_Centre()
c1.storm_list.append(s1)
assert(c1.already_exists('Jeff') == True)
assert(c1.already_exists('Dave') == False)
When running the test I get a "Can't instantiate abstract class" error. If anyone could help solve this error it would be much appreciated. thank you!
英文:
I'm currently testing this code using pytest from Storm_Centre.py
class Storm_Centre:
def __init__(self):
self.storm_list = []
def already_exists(self, name) -> bool:
for storm in self.storm_list:
if storm.name == name:
return True
return False
this code uses code from Storm.py
from abc import ABC, abstractmethod
class Storm(ABC):
def __init__(self, name, wind_speed):
self.name = name
self.wind_speed = wind_speed
super().__init__()
@abstractmethod
def calculate_classification(self):
pass
@abstractmethod
def get_advice(self):
pass
I'm testing the code in test_storm.py using the following code:
import pytest
from Storm import Storm
from Storm_Centre import Storm_Centre
def test_already_exists():
s1 = Storm('Jeff', 10)
c1 = Storm_Centre()
c1.storm_list.append(s1)
assert(c1.already_exists('Jeff') == True)
assert(c1.already_exists('Dave') == False)
When running the test I get a "Can't instantiate abstract class" error. If anyone could help solve this error it would be much appreciated. thank you!
答案1
得分: 1
以下是翻译好的部分:
创建抽象类的目的是它是一个永远不会实例化的类。相反,您必须实现它的一个非抽象子类,该子类实现了两个抽象方法,然后您可以实例化该子类。
当您为 Storm_Centre
和抽象的 Storm
类编写单元测试时,您需要定义一个非抽象的测试子类,类似于以下方式:
import pytest
from storm import Storm
from storm_centre import Storm_Centre
class TestStorm(Storm):
def calculate_classification(self):
return 4.2
def get_advice(self):
return "不要恐慌"
def test_already_exists():
s1 = TestStorm('Jeff', 10)
c1 = Storm_Centre()
c1.storm_list.append(s1)
assert(c1.already_exists('Jeff') == True)
assert(c1.already_exists('Dave') == False)
编辑:删除不必要的 __init__
方法。
PS:您可以使用 @chepner 的想法来使这个代码更加简洁:
class TestStorm(Storm):
pass
TestStorm.__abstractmethods__ = frozenset()
PPS:典型的Python文件名约定是使用小写:类 Storm
通常在文件 storm.py
中定义。
英文:
The point of creating an Abstract Class is that it's a class that you never instantiate. Instead, you have to implement a non-abstract subclass of it, which implements the two abstract methods, and then that's what you instantiate.
When you're writing the unit tests for Storm_Centre
and the abstract Storm
class itself, you'll need to define a non-abstract test sub class, something like this:
import pytest
from storm import Storm
from storm_centre import Storm_Centre
class TestStorm(Storm):
def calculate_classification(self):
return 4.2
def get_advice(self):
return "don't panic"
def test_already_exists():
s1 = TestStorm('Jeff', 10)
c1 = Storm_Centre()
c1.storm_list.append(s1)
assert(c1.already_exists('Jeff') == True)
assert(c1.already_exists('Dave') == False)
EDIT: removed the unnecessary __init__
method.
PS: you can use @chepner's idea to make this even smaller:
class TestStorm(Storm):
pass
TestStorm.__abstractmethods__ = frozenset()
PPS: the typical Python filename convention is to use lowercase: class Storm
is normally defined in file storm.py
.
答案2
得分: 1
抽象基类在测试目的上相对容易“规避”。
>>> Storm.__abstractmethods__
frozenset({'calculate_classification', 'get_advice'})
>>> Storm("Jeff", 10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 无法实例化抽象类 Storm,因为必须首先重写抽象方法 calculate_classification、get_advice
>>> Storm.__abstractmethods__ = frozenset()
>>> Storm("Jeff", 10)
<__main__.Storm object at 0x1011d0f50>
因此,为了避免定义具体的子类以进行实例化,您可以简单地将 __abstractmethods__
属性设置为空列表、集合等,然后尝试实例化它将成功(因为它不再报告必须首先重写的任何方法)。
英文:
Abstract base classes are fairly easy to "subvert" for testing purposes.
>>> Storm.__abstractmethods__
frozenset({'calculate_classification', 'get_advice'})
>>> Storm("Jeff", 10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Storm with abstract methods calculate_classification, get_advice
>>> Storm.__abstractmethods__ = frozenset()
>>> Storm("Jeff", 10)
<__main__.Storm object at 0x1011d0f50>
So to avoid the need to define a concrete subclass to instantiate, you can simply set the __abstractmethods__
attribute to an empty list, set, etc, and the attempt to instantiate it will succeed (because it no longer reports any methods that must be overriden first).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论