Test Runners have the single responsibility of taking a solution, running all tests and returning a standardized output. All interactions with the Exercism website are handled automatically and are not part of this specification.
two-fer
)./tmp
for temporary files (e.g. for compiling sources).results.json
file to the output directory.The test runner gets 100% CPU with 3GB of memory for a 20 second window per solution. After 20 seconds, the process is halted and reports a time-out.
We highly recommend following our Performance Best Practices document to reduce the chance of timeouts.
The following fields are supported in results.json
files:
key:
version
, type:number
, presence: required
version: 1, 2, 3
The version of the spec that this file adheres to:
1
: For tracks which test runner cannot provide information on individual tests.2
: For tracks which test runner can output individual test information. Minimal required version for tracks with Concept Exercises.3
: For tracks which test runner can link individual tests to a task.key:
status
, type:string
, presence: required
version: 1, 2, 3
The following overall statuses are valid:
pass
: All tests passedfail
: At least one test has the status fail
or error
error
: No test was executed (this usually means a compile error or a syntax error)The error
status should only be used if none of the tests were run.
For compiled languages this is generally a result of the code not being able to compile.
For interpreted languages, this is generally the result of a syntax error that stops the file from parsing.
key:
message
, type:string
, presence: required ifstatus
=error
, or whenstatus
=fail
andversion
=1
version: 1, 2, 3
Where the status is error
(no test was executed correctly), the top level message
key should be provided. It should provide the occurring error to the user. As it is the only piece of information a user will receive on how to debug their issue, it must be as clear as possible:
<solution-dir>/relative/path
instead of /full/path/to
, as that will include unhelpful ECR specific dataIn Ruby, in the case of a syntax error, we provide the runtime error and stack trace. In compiled languages, the compilation error should be provided.
The top level message
value is limited to 65535 characters.
The effective maximum length is less if the value contains multibyte characters.
When the status is not error
, either set the value to null
or omit the key entirely.
key:
tests
, type:array
, presence: required ifstatus
=fail
orstatus
=pass
version: 2, 3
This is an array of the test results, specified in the "Per-test" section below.
The tests MUST be returned in the order they are specified in the tests file. For languages that execute tests in a random order, this may mean re-ordering the results in line with the order specified in the tests file.
The rationale for this is that only the first failure is shown to students and therefore it is important that the correct failure is shown. Because tests are generally ordered in the tests file in a TDD way, and because for Practice Exercises the students see the tests file in the editor, aligning the results with the tests file is critical.
key:
name
, type:string
, presence: required
version: 2, 3
This is the name of the test in a human-readable format.
key:
test_code
, type:string
, presence: required if exercise is Concept Exercise
version: 2, 3
This MUST be present for Concept Exercises and SHOULD be present for Practice Exercises.
The difference in this requirement comes from that fact that students are not shown the tests in Concept Exercises, so solving the exercise may be impossible without the test_code
being shown, whereas the tests are shown for Practice Exercises.
This is the body of the command that is being tested. For example, the following Ruby test:
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
should return a test_code
value of:
"cart = ShoppingCart.new
cart.add(:STARIC)
cart.add(:MEDNEW)
cart.add(:MEDNEW)
assert_equal 'Newspaper, Rice', cart.items_list"
(with linebreaks replaced by \n
to make the JSON valid).
key:
status
, type:string
, presence: required
version: 2, 3
The following per-test statuses are valid:
pass
: The test passedfail
: The test failederror
: The test errored - that is, it did not return a valuekey:
message
, type:string
, presence: required ifstatus
isfail
orerror
version: 2, 3
The per-test message
key is used to return the results of a test with a status
of fail
or error
. It should be as human-readable as possible. Whatever is written here will be displayed to the student when their test does not pass. If there is no test failure message or error message, either set the value to null
or omit the key entirely. It is also permissible to output test suite output here. The message
value is not limited in length.
key:
output
, type:string
, presence: optional
version: 2, 3
The per-test output
key should be used to store and output anything that a user deliberately outputs for a test.
puts
in Ruby, print
in Python or Debug.WriteLine
in C#), or you may provide a method that the user may use (e.g. the Ruby Test Runner provides a user with a globally available debug
method that they can use, which has the same characteristics as the standard puts
method).key:
task_id
, type:number
, presence: optional
version: 3
Link a test to a specific task via the task's ID, which is the number used at the start of the task heading. Only link a test to a task if it can be linked to precisely one task.
At the moment, only Concept Exercises have well-defined tasks that you can link tests to, but this might change in the future.
For example, consider the following instructions.md
file:
# 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
...
These instructions defined two tasks:
The results.json
file could then have an entry like this:
{
"name": "Expected oven time in minutes",
"status": "pass",
"task_id": 1,
"test_code": "Assert.Equal(40, Lasagna.ExpectedMinutesInOven());"
}
This test is now linked to the first task: "Define the expected oven time in minutes". Note that the name does not have to match the task's description.
There are various ways tracks could implement this:
.meta/config.json
file) and merge this information into the generated results.json
file.These are example of what a valid results.json
file can look like for the different versions:
{
"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
}
]
}
When a student's solution fails a test, it should display something like:
Test Code:
<test_code>
Test Result:
<message>
When the solution passes a test, it should display something like:
Test Code:
<test_code>
All roads lead to Rome and there is no prescribed pattern to arrive at this. There are several approaches taken so far: