对于大量现有的(Fortran 90)代码尝试添加一些基本的unit testing是一个明智的方法,这些代码是仅在locking的系统上开发的,没有机会安装任何第三方框架。 我几乎局限于标准的Linux工具。 目前,使用非常有限的一组testing来在完整的系统级testing代码库,但是这非常耗时(需要多天的时间),所以在开发过程中很less使用
理想的情况是希望能够针对关键系统逐步添加目标testing,而不是一次尝试完全彻底地检查整个代码库。
以下面的示例模块为例,假定在Fortran中的Assert中详细描述了一个assert-typemacros的实现
MODULE foo CONTAINS FUNCTION bar() RESULT (some_output) INTEGER :: some_output some_output = 0 END FUNCTION bar END MODULE foo
有几种可能的方法值得思考,但是实施这些方法可能存在一些技术上的或挑战性的挑战:
为每个模块分配testing模块,如下所示,并有一个主testing运行器来调用每个模块中的每个function
MODULE foo_test CONTAINS SUBROUTINE bar_test() ! .... END SUBROUTINE bar_test() END MODULE foo_test
与上面类似的方法,但每个testing都有单独的可执行文件。 显而易见的好处是,单个故障不会终止所有的testing,但可能难以pipe理大量的testing可执行文件,并且可能需要大量额外的代码。
使用预处理器在每个模块中包含包含testing的主函数,例如,在具有C / C ++风格macros的 gfortran Fortran 90中(例如#define SUBNAME(x)s ## x)并使用构build脚本自动testingmain的存储在主代码文件中的预处理分隔符之间。
我曾尝试使用一些现有的Fortran框架(如在“Fortran中的unit testing框架为什么依赖于Ruby而不是Fortran本身?”中所述 ),但对于此特定项目,不能在其上安装其他工具我正在使用的系统。
在我看来,断言机制并不是Fortran单元测试的主要关注点。 正如你在回答中提到的那样,Fortran有几个单元测试框架,比如funit和FRUIT。
不过,我认为,主要问题是依赖关系的解决。 你可能有一个巨大的项目与许多相互依赖的模块,你的测试应该覆盖其他模块之一。 因此,您需要找到这些依赖关系并相应地构建单元测试。 一切都归结为编译可执行文件,断言的好处是非常有限的,因为你无论如何将需要定义你的测试,并自己做比较。
我们正在用Waf构建我们的Fortran应用程序, Waf自带了单元测试工具 。 现在,我不知道这是否可以使用,但唯一的要求是Python,几乎在任何平台上都可以使用它。 一个缺点是,测试依赖于返回代码,这是不容易从Fortran获得的,至少在Fortran 2008之前不能以便携的方式获得,建议在返回代码中提供停止代码。 所以我修改了在项目中成功的检查。 我不希望检查返回代码,而是希望测试写入一些字符串,并在输出中检查:
def summary(bld): """ Get the test results from last line of output:: Fortran applications can not return arbitrary return codes in a standarized way, instead we use the last line of output to decide the outcome of a test: It has to state "PASSED" to count as a successful test. Otherwise it is considered as a failed test. Non-Zero return codes that might still happen are also considered as failures. Display an execution summary: def build(bld): bld(features='cxx cxxprogram test', source='main.c', target='app') from waflib.extras import utest_results bld.add_post_fun(utest_results.summary) """ from waflib import Logs import sys lst = getattr(bld, 'utest_results', []) # Check for the PASSED keyword in the last line of stdout, to # decide on the actual success/failure of the test. nlst = [] for (f, code, out, err) in lst: ncode = code if not code: if sys.version_info[0] > 2: lines = out.decode('ascii').splitlines() else: lines = out.splitlines() if lines: ncode = lines[-1].strip() != 'PASSED' else: ncode = True nlst.append([f, ncode, out, err]) lst = nlst
另外,我按照惯例添加了测试,在构建脚本中只需提供一个目录,并且以_test.f90结尾的所有文件将被认为是单元测试,我们将尝试构建并运行它们:
def utests(bld, use, path='utests'): """ Define the unit tests from the programs found in the utests directory. """ from waflib import Options for utest in bld.path.ant_glob(path + '/*_test.f90'): nprocs = search_procs_in_file(utest.abspath()) if int(nprocs) > 0: bld( features = 'fc fcprogram test', source = utest, use = use, ut_exec = [Options.options.mpicmd, '-n', nprocs, utest.change_ext('').abspath()], target = utest.change_ext('')) else: bld( features = 'fc fcprogram test', source = utest, use = use, target = utest.change_ext(''))
您可以在Aotus库中找到定义的单元测试。 哪些在wscript中使用通过:
from waflib.extras import utest_results utest_results.utests(bld, 'aotus')
然后也可以从单元测试中构建子集,例如通过运行
./waf build --target=aot_table_test
在Aotus。 我们的测试收入有点微薄,但是我认为这次的基础设施交易会非常好。 测试可以简单地使用项目中的所有模块,并且可以轻松编译。
现在我不知道这是否适合你,但是我会更多地考虑你的测试在你的构建环境中的集成,而不是关于断言的东西。 在每个模块中都有一个测试程序是一个好主意,然后可以从测试程序中轻松调用。 我会尝试针对每个要测试的模块实现一个可执行文件,其中每个模块当然可以包含多个测试。