Pytest单元测试出现无法实例化抽象类错误。

huangapple go评论68阅读模式
英文:

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_classificationget_advice
&gt;&gt;&gt; Storm.__abstractmethods__ = frozenset()
&gt;&gt;&gt; Storm("Jeff", 10)
<__main__.Storm object at 0x1011d0f50>

因此,为了避免定义具体的子类以进行实例化,您可以简单地将 __abstractmethods__ 属性设置为空列表、集合等,然后尝试实例化它将成功(因为它不再报告必须首先重写的任何方法)。

英文:

Abstract base classes are fairly easy to "subvert" for testing purposes.

&gt;&gt;&gt; Storm.__abstractmethods__
frozenset({&#39;calculate_classification&#39;, &#39;get_advice&#39;})
&gt;&gt;&gt; Storm(&quot;Jeff&quot;, 10)
Traceback (most recent call last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
TypeError: Can&#39;t instantiate abstract class Storm with abstract methods calculate_classification, get_advice
&gt;&gt;&gt; Storm.__abstractmethods__ = frozenset()
&gt;&gt;&gt; Storm(&quot;Jeff&quot;, 10)
&lt;__main__.Storm object at 0x1011d0f50&gt;

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).

huangapple
  • 本文由 发表于 2023年5月25日 01:56:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76326254.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定