有没有好的框架可以帮助在Excel中单元测试代码?
答案 0 :(得分:5)
我假设您要在模块中对VBA代码进行单元测试 首先,即使有一个可用于VBA的良好单元测试框架,我怀疑测试会非常复杂,因为对工作簿本身的依赖性。如果你的代码与工作簿交互并且它的对象将成为一个彻头彻尾的噩梦,因为你无法真正模拟任何这一点:想象一下测试一个模块读取工作表中的数据并在另一个中创建一个图表......本质上,Excel工作簿将您的持久性,域和表示合并为一体 - 不利于测试 另一种情况是代码主要是面向计算的。如果该代码变得足够复杂以至于需要进行测试,那么您可能会考虑的一件事是将代码实际移到VBA之外,以使其可测试。我经常与拥有大量财务模型的客户合作,拥有大量的VBA,当我可以将VBA代码提取到C#并使其成为VSTO加载项时。好处是我可以测试代码,并在Visual Studio中工作,而不是VBA IDE。
答案 1 :(得分:3)
答案 2 :(得分:3)
我使用Debug.Assert
。我在模块中组织测试。在每个模块中,我维护一个Sub RunAll
来调用该模块中的所有测试方法。
要在项目中运行所有测试,我有一个带有RunAll的模块AllTests,它在所有测试模块中调用RunAll。
简单而且没有任何大惊小怪。
答案 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批准的单元测试方法:
以下是您需要做的快速摘要:
创建一个数据源,其中包含您在测试方法中使用的值。数据源可以是运行测试的计算机上注册的任何类型。
向测试类添加一个私有TestContext字段和一个公共TestContext属性。
创建一个单元测试方法并向其添加DataSourceAttribute属性。
使用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)