测试运行器只有一个职责:接收提交的解答,运行所有测试,并返回标准化的输出。 与 Exercism 网站的所有交互都会自动处理,不属于本规范的范围。
two-fer)。/tmp。results.json 文件写入输出目录。针对每个提交的解答,测试运行器都有 20 秒的时间,期间可占用 100% 的 CPU 和 3GB 内存。 20 秒后,进程会被中止,并报告超时。
我们强烈建议遵循性能最佳实践文档,以降低超时发生的可能性。
results.json 文件支持以下字段:
键:
version,类型:number,是否必需:必需
版本:1、2、3
本文件所遵循的规范版本:
1:适用于测试运行器无法提供单个测试信息的赛道。2:适用于测试运行器能够输出单个测试信息的赛道。含有概念练习的赛道至少需要此版本。3:适用于测试运行器能够把单个测试关联到某个任务的赛道。键:
status,类型:string,是否必需:必需
版本:1、2、3
以下整体状态有效:
pass:所有测试通过fail:至少有一个测试的状态为 fail 或 error
error:没有执行任何测试(这通常意味着编译错误或语法错误)error 状态只应在所有测试都出错时使用。
对编译型语言来说,这通常是代码无法编译导致的。
对解释型语言来说,这是运行时错误,比如导致文件无法解析的语法错误。
键:
message,类型:string,是否必需:当status=error时必需,或当status=fail且version=1时必需
版本:1、2、3
当状态为 error(没有正确执行任何测试)时,应提供顶层 message 键。它应向用户说明发生的错误。由于这是用户了解如何调试问题的唯一信息,它必须尽可能清晰:
<solution-dir>/relative/path 的形式,而不是 /full/path/to,因为后者会包含对用户无用的 ECR 特定数据在 Ruby 中,遇到语法错误时,我们会提供运行时错误和堆栈跟踪。在编译型语言中,应提供编译错误。
顶层 message 的值限制为 65535 个字符。
如果值包含多字节字符,有效最大长度会更小。
当状态不是 error 时,要么把值设为 null,要么完全省略该键。
键:
tests,类型:array,是否必需:当status=fail或status=pass时必需
版本:2、3
这是一个测试结果数组,具体见下文“每个测试”一节。
测试必须按测试文件中指定的顺序返回。 对于以随机顺序执行测试的语言,这可能意味着要按照测试文件中指定的顺序重新排列结果。
这样做的理由是,只会向学生显示第一个失败,因此显示正确的失败非常重要。由于测试文件中的测试通常按 TDD 方式排序,而且在实践练习中学生会看到编辑器里的测试文件,所以让结果与测试文件保持一致至关重要。
键:
name,类型:string,是否必需:必需
版本:2、3
这是测试的名称,采用人类可读的格式。
键:
test_code,类型:string,是否必需:当练习是概念练习时必需
版本:2、3
对概念练习来说,此键必须存在;对实践练习来说,应当存在。
这一要求上的差异源于:概念练习中不会向学生显示测试,因此如果不显示 test_code,可能就无法完成该练习;而实践练习会显示测试。
这是被测试命令的主体。例如,下面这个 Ruby 测试:
def test_duplicate_items_uniqs_list
cart = ShoppingCart.new
cart.add(:STARIC)
cart.add(:MEDNEW)
cart.add(:MEDNEW)
assert_equal 'Newspaper, Rice', cart.items_list
end
应返回如下 test_code 值:
"cart = ShoppingCart.new
cart.add(:STARIC)
cart.add(:MEDNEW)
cart.add(:MEDNEW)
assert_equal 'Newspaper, Rice', cart.items_list"
(其中换行符会被替换为 \n 以保证 JSON 有效)。
键:
status,类型:string,是否必需:必需
版本:2、3
以下单个测试状态有效:
pass:测试通过fail:测试失败error:测试出错,即它没有返回值键:
message,类型:string,是否必需:当status为fail或error时必需
版本:2、3
单个测试的 message 键用于返回 status 为 fail 或 error 的测试结果。它应尽可能人类可读。这里写的内容会在学生测试未通过时显示给他们。如果没有测试失败消息或错误消息,要么把值设为 null,要么完全省略该键。也允许在这里输出测试套件的输出。message 的值没有长度限制。
键:
output,类型:string,是否必需:可选
版本:2、3
单个测试的 output 键用于存储并输出用户为某项测试有意输出的任何内容。
puts、Python 的 print 或 C# 的 Debug.WriteLine),也可以提供一种供用户使用的方法(例如 Ruby 测试运行器为用户提供了一个全局可用的 debug 方法,其特性与标准的 puts 方法相同)。键:
task_id,类型:number,是否必需:可选
版本:3
通过任务的 ID 把测试关联到特定任务,该 ID 是任务标题开头使用的数字。只有当某个测试能精确关联到一个任务时,才把它关联到任务。
目前,只有概念练习有明确定义、可供关联测试的任务,但将来可能会改变。
例如,看下面这个 instructions.md 文件:
# Instructions
You're going to write some code to help Lucian cook an exquisite lasagna from his favorite cook book.
## 1. Define the expected oven time in minutes
...
## 2. Calculate the remaining oven time in minutes
...
这些说明定义了两个任务:
那么 results.json 文件可以有这样一个条目:
{
"name": "Expected oven time in minutes",
"status": "pass",
"task_id": 1,
"test_code": "Assert.Equal(40, Lasagna.ExpectedMinutesInOven());"
}
这个测试现在关联到了第一个任务:“以分钟为单位定义预期的烤箱时间”。注意,名称不必与任务的描述一致。
赛道可以有多种方式实现这一点:
.meta/config.json 文件),再把这一信息合并进生成的 results.json 文件。以下是不同版本下有效的 results.json 文件的示例:
{
"version": 1,
"status": "fail",
"message": "Failed: test_answer\nExpected: 42, actual: 3"
}
{
"version": 2,
"status": "fail",
"message": null,
"tests": [
{
"name": "Test that the thing works",
"status": "fail",
"message": "Expected 42 but got 123123",
"output": "Debugging information output by the user",
"test_code": "assert_equal 42, answerToTheUltimateQuestion()"
}
]
}
{
"version": 3,
"status": "fail",
"message": null,
"tests": [
{
"name": "Test that the thing works",
"status": "fail",
"message": "Expected 42 but got 123123",
"output": "Debugging information output by the user",
"test_code": "assert_equal 42, answerToTheUltimateQuestion()",
"task_id": 1
}
]
}
当学生提交的解答未通过某项测试时,应显示类似这样的内容:
Test Code:
<test_code>
Test Result:
<message>
当提交的解答通过某项测试时,应显示类似这样的内容:
Test Code:
<test_code>
条条大路通罗马,达成这一点并没有规定的模式。 到目前为止,已经采用过几种做法: