测试

本地设置开发环境

请按照我们的安装指南设置一个合适的环境来从源代码构建statsmodels。我们建议您在一个虚拟环境中使用statsmodels的开发版本来进行开发,运行以下命令:

python -m venv .venv
python -m pip install -e ".[develop]"

从git仓库的根目录开始。标志-e用于可编辑。

此命令编译C代码,并通过创建从您的Python环境库到statsmodels源代码的链接,将statsmodels添加到您的激活Python环境中。因此,对纯Python代码的更改将立即对用户可用,无需重新安装。对C代码或Cython代码的更改需要重新运行python -m pip install -e ".[develop]",然后这些更改才会可用。

测试驱动开发

我们努力遵循测试驱动开发 (TDD)模式。 所有添加到主代码库的模型或统计函数都应尽可能与现有的统计包进行测试。

pytest简介

与许多包一样,statsmodels 使用 pytest 测试系统 以及 numpy.testing 中的便捷扩展。Pytest 将找到任何以 testTest(仅限类)开头的文件、目录、函数或类名。测试函数应以 test 开头,测试类应以 Test 开头。这些函数和类应放置在名称以 test 开头的文件中,位于名为 tests 的目录中。

运行测试

测试通过命令行运行,调用 pytest。直接使用 pytest 运行测试需要按照上述描述,使用 python -m pip install -e ".[develop]" 安装 statsmodels。

测试可以在不同的粒度级别上运行:

  • 项目级别,运行所有测试。运行整个测试套件很慢,通常只有在对statsmodels进行深度更改时才需要这样做。

pytest statsmodels
  • 文件夹级别,运行文件夹下的所有测试

pytest statsmodels/regression/tests
  • 文件级别,运行文件中的所有测试

pytest statsmodels/regression/tests/test_regression.py
  • 类级别,运行类中的所有测试

pytest statsmodels/regression/tests/test_regression.py::TestOLS
  • 测试级别,运行单个测试。第一个示例在类中运行一个测试。第二个运行一个独立的测试。

pytest statsmodels/regression/tests/test_regression.py::TestOLS::test_missing
pytest statsmodels/regression/tests/test_regression.py::test_ridge

如何编写测试

NumPy 提供了关于使用 pytest 和 NumPy 扩展进行单元测试的良好介绍,详情请参见这里。值得一读以了解更多细节。 在这里,我们将记录一些我们遵循的值得提及的约定。通常我们希望一次性测试整个模型,而不仅仅是一个函数,例如。以下是简化版的 test_discrete.py。在这种情况下,需要测试具有不同选项的几个不同模型。测试看起来像这样

from numpy.testing import assert_almost_equal
import statsmodels.api as sm
from results.results_discrete import Spector

class CheckDiscreteResults(object):
    """
    res2 are the results. res1 are the values from statsmodels
    """

    def test_params(self):
        assert_almost_equal(self.res1.params, self.res2.params, 4)

    decimal_tvalues = 4
    def test_tvalues(self):
        assert_almost_equal(self.res1.params, self.res2.params, self.decimal_tvalues)

    # ... as many more tests as there are common results

class TestProbitNewton(CheckDiscreteResults):
    """
    Tests the Probit model using Newton's method for fitting.
    """

    @classmethod
    def setup_class(cls):
        # set up model
        data = sm.datasets.spector.load()
        data.exog = sm.add_constant(data.exog)
        cls.res1 = sm.Probit(data.endog, data.exog).fit(method='newton', disp=0)

        # set up results
        res2 = Spector.probit
        cls.res2 = res2

        # set up precision
        cls.decimal_tvalues = 3

    def test_model_specifc(self):
        assert_almost_equal(self.res1.foo, self.res2.foo, 4)

主要的工作类是 CheckDiscreteResults。注意,我们可以在子类 TestProbitNewton 中将 tvalues 的精度级别设置为不同于默认值。所有的测试类都有一个 @classmethod 方法叫做 setup_class。否则,pytest 会在每个测试方法之前重新实例化类。如果模型的拟合过程耗时较长,那么这显然是不希望的。最后,我们在底部有一个脚本,以便我们可以运行测试,应该运行 Python 文件。

测试结果

测试结果是上述示例的最后一部分。对于许多测试,尤其是针对模型的测试,有许多结果需要进行测试。因此,将硬编码的结果与实际测试分开,以提高测试的可读性是有意义的。如果只有少数结果,则不需要分开结果。我们通常从其他统计软件包中获取结果。重要的是要记录结果的来源以及它们与我们得到的结果可能存在差异的原因。每个测试文件夹都有一个结果子目录。考虑离散模型的文件夹结构:

tests/
    __init__.py
    test_discrete.py
    results/
        __init__.py
        results_discrete.py
        nbinom_resids.csv

如何最佳地组织结果取决于您。在离散模型示例中,您会注意到有一些基于特定数据集的结果类,并且有一种方法可以为该数据集加载不同的模型结果。如果将结果放在类本身中比加载文本文件更方便,您还可以包含保存结果的文本文件,以便结果类加载。

加速完整运行

运行完整的测试套件很慢。幸运的是,只有在进行底层更改(例如,对statsmodels.base进行更改)时才需要运行完整的套件。有两种方法可以在需要时加快完整测试套件的运行速度。

  • 使用 pytest-xdist 包

python -m pip install pytest-xdist
export MKL_NUM_THREADS=1
export OMP_NUM_THREADS=1
pytest -n auto statsmodels
  • 使用 --skip-slow 跳过慢速测试

pytest --skip-slow statsmodels

您可以将这两种方法结合起来以加快运行速度。

export MKL_NUM_THREADS=1 && export OMP_NUM_THREADS=1
pytest -n auto --skip-slow statsmodels

The test() 方法

statsmodels及其所有子模块的根目录都暴露了一个test()方法,该方法可以用于运行包中的所有测试(statsmodels.test())或模块中的测试(statsmodels.regression.test())。即使statsmodels不是使用上述的可编辑标志安装的,此方法也允许从安装的副本中运行测试。此方法在发布构建中用于测试轮子是必需的,但不推荐用于开发。

使用此方法,所有测试均使用以下方式运行:

import statsmodels.api as sm
sm.test()

子模块测试使用以下方式运行:

sm.discrete.test()

test([extra_args, exit])

运行测试套件


Last update: Oct 16, 2024