有没有好的单元测试包excel

时间:2010-01-16 14:55:25

标签: unit-testing excel

有没有好的框架可以帮助在Excel中单元测试代码?

7 个答案:

答案 0 :(得分:5)

我假设您要在模块中对VBA代码进行单元测试 首先,即使有一个可用于VBA的良好单元测试框架,我怀疑测试会非常复杂,因为对工作簿本身的依赖性。如果你的代码与工作簿交互并且它的对象将成为一个彻头彻尾的噩梦,因为你无法真正模拟任何这一点:想象一下测试一个模块读取工作表中的数据并在另一个中创建一个图表......本质上,Excel工作簿将您的持久性,域和表示合并为一体 - 不利于测试 另一种情况是代码主要是面向计算的。如果该代码变得足够复杂以至于需要进行测试,那么您可能会考虑的一件事是将代码实际移到VBA之外,以使其可测试。我经常与拥有大量财务模型的客户合作,拥有大量的VBA,当我可以将VBA代码提取到C#并使其成为VSTO加载项时。好处是我可以测试代码,并在Visual Studio中工作,而不是VBA IDE。

答案 1 :(得分:3)

互联网上有几个实验:EUnitVBAUnit。这些项目似乎都没有活跃的社区,因此它们不太可能像(如)JUnit或NUnit那样强大。但它们可能足以满足您的目的。

答案 2 :(得分:3)

我使用Debug.Assert。我在模块中组织测试。在每个模块中,我维护一个Sub RunAll来调用该模块中的所有测试方法。

要在项目中运行所有测试,我有一个带有RunAll的模块AllTests,它在所有测试模块中调用RunAll。

简单而且没有任何大惊小怪。

MSDN article on VBA Debug.Assert

答案 3 :(得分:1)

我写了一篇博客文章,介绍如何轻松地使用Python的单元测试基础设施来编写VBA的单元测试,以及一般的单元逻辑:https://www.zoomeranalytics.com/blog/unittests-for-microsoft-excel

实质上,您可以测试以下VBA函数:

Function mysum(x, y) As Double
    mysum = x + y
End Function

在Python中使用以下单元测试:

import unittest
import xlwings as xw

class TestMyBook(unittest.TestCase):
    def setUp(self):
        # setUp will be called before the execution of each unit test
        self.wb = xw.Book('mybook.xlsm')  # map workbook
        self.mysum = self.wb.macro('Module1.mysum')  # map function

    def test_mysum(self):
        result = self.mysum(1, 2)  # get result from VBA
        self.assertAlmostEqual(3, result)  # test if result corresponds to the expected value

if __name__ == '__main__':
    # This allows us to easily run the tests from the command prompt
    unittest.main()

答案 4 :(得分:1)

我很惊讶在其他建议中找不到Rubberduck。它是VBE的COM插件(适用于所有MS Office主机:Excel,Word,PowerPoint,Access),开源和主动开发。它的主要功能是单元测试和代码检查。

简要说明,取自上面链接的维基页面:

  

为什么选择Rubberduck?

     

因为我们想要一个宠物项目,这个很有趣,也很有挑战性。   我们的想法是尽我们所能在VBA和VBA中进行编程   重构传统的VBA代码,像现代一样令人愉快   Visual Studio的版本。 VBE已经有了VB6的感觉   我记得,Rubberduck把它带到了其他地方。

     

这一切都始于Code Review,其中有一篇关于VBA单元测试的文章。   它发展成为100%VBA工作解决方案,然后我们想拥有   这个单元测试功能内置于IDE中 - COM插件   解决方案始于它。

     

可以访问COM加载项中的整个VBE对象模型   添加自定义菜单,工具栏和可停靠的窗口,我们想解析   并检查代码,并修复问题 - 或者至少指出它们。

     

然后我们只是想让我们打算实现我们想要实现的一切   扩展VBE。我们添加了更轻松地导航代码的方法,列表   todo项目,然后我们有想法,如集成源代码控制,   实现一些重构,使用Stack Exchange API创建   并将您的VBA代码发布为代码审查问题。

我已经使用它大约半年了(主要是在Excel 2010下),它非常稳定和有用。因此,我会推荐它。

答案 5 :(得分:0)

这是MS批准的单元测试方法:

https://docs.microsoft.com/en-us/visualstudio/test/how-to-create-a-data-driven-unit-test?view=vs-2015&redirectedfrom=MSDN

以下是您需要做的快速摘要:

  1. 创建一个数据源,其中包含您在测试方法中使用的值。数据源可以是运行测试的计算机上注册的任何类型。

  2. 向测试类添加一个私有TestContext字段和一个公共TestContext属性。

  3. 创建一个单元测试方法并向其添加DataSourceAttribute属性。

  4. 使用DataRow索引器属性检索您在测试中使用的值。

答案 6 :(得分:0)

FlyingKoala项目可以帮助进行Excel公式的单元测试。

[FlyingKoala]为xlwings提供了扩展功能。

这里是一个示例,用于从“内部” Excel(如FlyingKoala库中找到)对公式(和公式网络)进行单元测试;

import unittest
import logging

import xlwings as xw
from flyingkoala import FlyingKoala
from pandas import DataFrame
from numpy import array
from numpy.testing import assert_array_equal

logging.basicConfig(level=logging.ERROR)

class Test_equation_1(unittest.TestCase):

    def setUp(self):

        self.workbook_name = r'growing_degrees_day.xlsm'

        if len(xw.apps) == 0:
            raise "We need an Excel workbook open for this unit test."

        self.my_fk = FlyingKoala(self.workbook_name, load_koala=True)
        self.my_fk.reload_koala('')
        self.equation_name = xw.Range('Equation_1')

        if self.equation_name not in self.my_fk.koala_models.keys():
            model = None
            wb = xw.books[self.workbook_name]
            wb.activate()
            for name in wb.names:
                self.my_fk.load_model(self.equation_name)
                if self.equation_name == name.name:
                    model = xw.Range(self.equation_name)
                    self.my_fk.generate_model_graph(model)

            if model is None:
                return 'Model "%s" has not been loaded into cache, if named range exists check spelling.' % self.equation_name


    def test_Equation_1(self):
        """First type of test for Equation_1"""

        xw.books[self.workbook_name].sheets['Growing Degree Day'].activate()

        goal = xw.books[self.workbook_name].sheets['Growing Degree Day'].range(xw.Range('D2'), xw.Range('D6')).options(array).value

        tmin = xw.books[self.workbook_name].sheets['Growing Degree Day'].range(xw.Range('B2'), xw.Range('B6')).options(array).value
        tmax = xw.books[self.workbook_name].sheets['Growing Degree Day'].range(xw.Range('C2'), xw.Range('C6')).options(array).value
        inputs_for_DegreeDay = DataFrame({'T_min': tmin, 'T_max': tmax})
        result = self.my_fk.evaluate_koala_model('Equation_1', inputs_for_DegreeDay).to_numpy()

        assert_array_equal(goal, result)


    def test_VBA_Equation_1(self):
        """
        The function definition being called;

        Function VBA_Equation_1(T_min As Double, T_max As Double) As Double
            VBA_Equation_1 = Application.WorksheetFunction.Max(((T_max + T_min) / 2) - 10, 0)
            End Function
    """

        goal = 20

        vba_equation_1 = xw.books[self.workbook_name].macro('VBA_Equation_1')
        result = vba_equation_1(20.0, 40.0)

        self.assertEqual(goal, result)

这是来自“外部” Excel的示例(在FlyingKoala库中找到);

import io
import sys
import unittest

import xlwings as xw
import flyingkoala
from flyingkoala import *

sys.setrecursionlimit(3000)

workbook_path = 'C:\\Users\\yourself\\Documents\\Python\\example\\'
workbook_name = r'example.xlsm'

class Test_equation_1(unittest.TestCase):
    """Unit testing Equation_1 in Python
    Using;
    - named ranges to discover the address of the formula
    - Python as the calculation engine
    This approach requires;
    - Workbook to be tested needs to be open. In this case it's example.xlsm from the example directory in FlyingKoala++
        ++This needs to be changed so that FlyingKoala can use Koala to figure out the model for itself
    """

    equation_name = 'Equation_1'

    books = xw.books
    workbook = reload_koala('%s%s' % (workbook_path, workbook_name), ignore_sheets=['Raw Data'])

    equation_1 = None
    selected_book = None

    # find the equation, and its address
    for book in books:
        if book.name == workbook_name:
            selected_book = book

            for named_range in book.names:
                if named_range.name == equation_name:
                    equation_1 = named_range.refers_to_range
                    # parse the equation into Python
                    generate_model_graph(equation_1)

    def test_1(self):
        """First type of test for Equation_1"""

        # define test case inputs
        case_00 = {'terms' : {'T_base': 10, 'T_min': 10, 'T_max': 20}, 'result' : 5.0}

        # Do a calc
        result = flyingkoala.evaluate_koala_model_row(self.equation_name, case_00['terms'], flyingkoala.koala_models[self.equation_name])

        # test the result of the calculation
        self.assertEqual(case_00['result'], result)