Для преподавателей
Как создавать задачи с автопроверкой кода
Как сделать практическое задание, которое платформа проверит сама: проверка вывода, ввод данных, функции, перехват печати и защита от подгонки ответа. Примеры — на Python, JavaScript и C++: язык переключается в любом примере и меняется сразу на всей странице. Каждый пример прогнан в нашей песочнице — копируйте как есть.
Где создаются задачи
- Учительская → «Мои курсы» → курс → темы и уроки → откройте урок.
- Под текстом урока — блок «Практика», кнопка «Добавить практику».
- В редакторе практики: «Добавить задачу» — задание с кодом, «Добавить тест» — вопросы с вариантами ответа, «JSON практики» — вставить всё разом (см. ниже).
- У задачи заполните название, «Условие задачи», язык, «Заготовку кода» и способ проверки — о нём вся эта страница.
Как работает проверка
У ученика под редактором две кнопки:
- «Проверить» запускает мини-тесты. Это быстрая проверка во время работы, задачу она не засчитывает.
- «Отправить решение» появляется после успешной проверки и запускает полные тесты. Задача считается решённой только после неё. Если полные тесты пустые, при отправке используются мини-тесты.
Главное, что нужно запомнить: код ученика и ваши тесты склеиваются в один файл — сначала код ученика, потом тесты. Поэтому в тестах доступны все переменные и функции, которые написал ученик. Если код ученика не компилируется или падает с ошибкой, проверка не засчитывается, а ученик видит последнюю строку ошибки.
Какой способ выбрать
| Что нужно проверить | Способ |
|---|---|
| Программа печатает готовую фразу | По выводу, мягкое сравнение |
| Программа с вводом, данные вводит сам ученик | По выводу + «Ввод (stdin)» + {} |
| Результат вычисления | Тесты, рецепт 1 |
| Функция | Тесты, рецепт 2 |
| Программа с вводом на ваших данных | Тесты + подмена ввода, рецепт 3 |
| Функция, которая печатает | Тесты + перехват печати, рецепт 4 |
| Чтобы нельзя было просто напечатать готовый ответ | Функции; в Python ещё run_variant |
| Теория, вывод проверить нельзя | Самопроверка |
Сомневаетесь — берите тесты. Они проверяют несколько случаев, а не один, и показывают ученику, что именно не так.
Python, JavaScript, C++: чем отличаются
Идея везде одна — equals для проверок и printResults() для итога. Но у каждого языка свои правила, и почти все ошибки учителей — отсюда.
| Python | JavaScript | C++ | |
|---|---|---|---|
| Что пишет ученик в режиме «по выводу» | Программу целиком | Программу целиком | Только тело main(): без int main()и без своих #include |
| Ввод в режиме «по выводу» | input() | input() — наша функция; prompt и readline нет | cin, getline |
| Тесты | Код после кода ученика | Код после кода ученика | Ученик пишет функции, в тестах обязателен int main() |
Без printResults() | Не засчитается | Допишется сам | Не засчитается |
| Ввод на ваших данных в тестах | Первая строка тестов | function input() в тестах | Функция solve() + подмена cin/cout |
| Подключения | import работает | require и import недоступны | iostream, vector, string, algorithm, cmath, sstream и using namespace std подключены сами |
| Печать в поток ошибок = провал | предупреждения (warnings) | console.error | cerr |
| Особенности разбора | Строка "5" и число 5 выглядят одинаково | Массивы и объекты сравниваются по содержимому | true/false показываются как 1/0, дроби — округлёнными |
Проверка по выводу
Включите переключатель «Проверка по выводу» и выберите режим. Платформа один раз запускает программу ученика и сравнивает всё, что она напечатала, с полем «Ожидаемый вывод».
| Режим | Как сравнивает | Когда брать |
|---|---|---|
| Точное совпадение | Символ в символ. Не важны только пробелы и пустые строки в самом начале и в конце. Два пробела вместо одного — уже ошибка. | Числа, строгие форматы |
| Мягкое сравнение | Не важны регистр, лишние пробелы, пустые строки и знаки , . ! ? ; : в конце слов. «итого 5» засчитается для «Итого: 5.» Но «3,14» и «3.14» — разные ответы: знаки внутри числа не убираются. | Фразы и предложения |
| Самопроверка | Засчитывается любой запуск без ошибки. В поле пишете пример решения или объяснение — ученик увидит его после запуска. Пустая или нетронутая заготовка не засчитывается. | Теория, крайний случай |
Пример: «Привет, мир»
Условие: «Выведите фразу Hello, World!». Режим — мягкое сравнение, «Ввод (stdin)» выключен.
Hello, World!print("Hello, World!")Засчитаются и Hello, World!, и hello world. В условии всегда показывайте дословно, что должно быть напечатано, — иначе ученик будет угадывать формат.
Ввод данных учеником и {}
Включите «Ввод (stdin)» — у ученика под редактором появится поле ввода. Каждая строка поля — одна строка ввода программы. В ожидаемом выводе {} заменяется на введённые строки по порядку.
Условие: «Спросите имя и возраст и выведите: Привет, имя! Тебе возраст лет». Режим — точное совпадение.
Привет, {}! Тебе {} летname = input('Как тебя зовут? ')
age = input('Сколько тебе лет? ')
print(f'Привет, {name}! Тебе {age} лет')Ученик вводит в поле две строки, Аня и 12, — ожидается Привет, Аня! Тебе 12 лет, задача засчитана.
- Данные выбирает сам ученик, поэтому так проверяется только формат вывода. Посчитать по этим данным ответ (например, сумму) {} не умеет — для этого нужны тесты с подменой ввода.
- Если ученик не заполнил поле, а программа читает ввод, в Python и JavaScript он увидит ошибку «No more input data», а в C++ переменные останутся пустыми. Напишите в условии, что вводить.
- Пустые строки в поле ввода отбрасываются.
- Текст-приглашение из
input('...')в вывод не попадает — не пишите его в ожидаемый вывод.
Проверка через тесты
Выключите «Проверку по выводу» — появятся поля «Мини-тесты» и «Полные тесты». Тесты — обычный код на языке задачи, который выполняется сразу после кода ученика. В нём есть две готовые функции:
equals(получено, ожидается, "название")— сравнивает два значения. Название проверки ученик увидит в разборе: пишите его по-русски и понятно.printResults()— подводит итог. Ровно один раз, последней строкой. В Python и C++ без неё задача не засчитается никогда, даже при верном решении.
equals(total, 3600, "стоимость покупки")
equals(is_even(0), True, "ноль — чётное число")
printResults()Советы:
- Мини-тесты — 1–3 главные проверки. Полные — 4–8, с краевыми случаями: 0, отрицательные числа, пустая строка, граница диапазона.
- Заготовка должна проваливать тесты: результат-заглушка (
None,null,return 0), функция без тела. - Дроби сравнивайте после округления:
0.1 + 0.2не равно0.3. В C++ разбор покажет «0.3» и «0.3» — ученик не поймёт, в чём дело. - Ученик видит названия проверок и ожидаемые значения в упавших проверках — не прячьте в них ничего, чего ему нельзя знать.
Результат вычисления
Условие для ученика: Товар стоит price рублей, купили count штук. Посчитайте стоимость покупки и сохраните её в переменную total.
price = 1200
count = 3
# посчитайте стоимость покупки
total = Noneprice = 1200
count = 3
total = price * countequals(total, 3600, "1200 ₽ × 3 шт.")
printResults()equals(type(total).__name__, "int", "в total лежит число, а не строка")
equals(total, 3600, "1200 ₽ × 3 шт.")
printResults()Если сдать нетронутую заготовку, ученик увидит:
До темы функций это основной формат: заготовка задаёт исходные данные, ученик кладёт ответ в переменную с заранее названным именем, тесты проверяют эту переменную.
Функция
Условие для ученика: Напишите функцию is_even(n), которая возвращает True, если число n чётное, и False, если нечётное.
def is_even(n):
# верните True, если n чётное
passdef is_even(n):
return n % 2 == 0equals(is_even(4), True, "is_even(4)")
equals(is_even(7), False, "is_even(7)")
printResults()equals(is_even(4), True, "is_even(4)")
equals(is_even(7), False, "is_even(7)")
equals(is_even(0), True, "ноль — чётное число")
equals(is_even(-3), False, "отрицательное нечётное")
equals(is_even(-10), True, "отрицательное чётное")
printResults()Если сдать нетронутую заготовку, ученик увидит:
Функцию можно вызвать сколько угодно раз с разными аргументами — поэтому это самый надёжный вид задач.
Ввод и вывод в тестах
В режиме тестов программа ученика не получает данных из поля ввода, а всё, что она печатает, уходит в общий вывод вперемешку с вашими проверками. Чтобы проверить программу с вводом, тест должен сам подать ей данные и забрать то, что она напечатала. В каждом языке это делается по-своему.
Готовая первая строка — подменяет ввод и перехватывает печать:
import builtins as _b, sys as _s, io as _io; _it = iter(['2', '3']); _b.input = lambda *a: next(_it); _out = _io.StringIO(); _s.stdout = _outiter(['2', '3'])— ответы наinput()по порядку, всегда строками._b.input = lambda *a: next(_it)— подменяетinput(); текст-приглашение игнорируется._s.stdout = _out— всё, что напечатает ученик, копится в_out.
Дальше, уже после кода ученика, верните печать на место — _s.stdout = _s.__stdout__ — и сравните _out.getvalue().strip() с ожидаемым. Если печать не вернуть, итог проверок тоже уйдёт в _out и задача не засчитается. Без подмены input() в тестах падает с «EOF when reading a line».
Программа с вводом на ваших данных
Условие для ученика: Программа получает два целых числа, каждое на отдельной строке, и выводит их сумму.
# прочитайте два числа и выведите их суммуa = int(input('Первое число: '))
b = int(input('Второе число: '))
print(a + b)import builtins as _b, sys as _s, io as _io; _it = iter(['2', '3']); _b.input = lambda *a: next(_it); _out = _io.StringIO(); _s.stdout = _out
_s.stdout = _s.__stdout__
equals(_out.getvalue().strip(), '5', 'ввод 2 и 3')
printResults()import builtins as _b, sys as _s, io as _io; _it = iter(['-7', '10']); _b.input = lambda *a: next(_it); _out = _io.StringIO(); _s.stdout = _out
_s.stdout = _s.__stdout__
equals(_out.getvalue().strip(), '3', 'ввод -7 и 10')
printResults()Если сдать нетронутую заготовку, ученик увидит:
- Программа запускается один раз, поэтому один набор тестов — это один набор входных данных. Удобный приём: в мини-тестах одни данные, в полных — другие, как здесь. Нужно много случаев — сформулируйте задачу через функцию.
- Если программа попросит больше строк, чем вы дали, ученик увидит невнятное «Error during execution:». Пишите в условии, сколько строк вводится.
- Вывод в несколько строк удобно сравнивать списком:
equals(_out.getvalue().split(), ['1', '2', '3'], "числа 1, 2, 3"). - Нужно только перехватить печать, без ввода, — оставьте в первой строке
import sys as _s, io as _io; _out = _io.StringIO(); _s.stdout = _out.
Функция, которая печатает
Условие для ученика: Напишите функцию draw_line(n), которая печатает строку из n звёздочек.
def draw_line(n):
# напечатайте n звёздочек одной строкой
passdef draw_line(n):
print('*' * n)import io, contextlib
def printed(func, *args):
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
func(*args)
return buf.getvalue().strip()
equals(printed(draw_line, 3), '***', 'draw_line(3)')
printResults()import io, contextlib
def printed(func, *args):
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
func(*args)
return buf.getvalue().strip()
equals(printed(draw_line, 3), '***', 'draw_line(3)')
equals(printed(draw_line, 1), '*', 'draw_line(1)')
equals(printed(draw_line, 10), '**********', 'draw_line(10)')
equals(draw_line(2), None, 'функция печатает, а не возвращает')
printResults()Если сдать нетронутую заготовку, ученик увидит:
Здесь первая строка не нужна: функцию вызывают сами тесты, и печать одного вызова перехватывает contextlib.redirect_stdout. Обычные import в теле тестов работают как в любом файле. Последняя проверка ловит частую ошибку новичков — return вместо print.
Защита от подгонки ответа
В задаче с готовыми данными ученик может прочитать ожидаемый ответ в разборе и просто напечатать его. Надёжнее всего — функции: тесты вызывают их с данными, которых ученик заранее не знает. В Python для задач без функций есть ещё run_variant: он перезапускает код ученика, подменив значение переменной.
- Первая строка тестов — ровно
# __SUDOTEACH_TESTS__. По ней платформа отделяет код ученика от тестов. run_variant(seconds=59)меняет первое присваиваниеseconds = ...на верхнем уровне кода ученика, запускает код заново и возвращает напечатанное (без пробелов по краям).- Нет такой строки или код упал — вернётся текст в угловых скобках, например
<в коде нет строки seconds = ...>, и ученик увидит его в разборе. - С подменой ввода через первую строку не сочетается: обоим нужна первая строка.
run_variant: та же программа на других данных
Условие для ученика: В переменной seconds — количество секунд. Выведите это время в формате часы:минуты:секунды, например 3725 → 1:2:5.
seconds = 3725
# выведите время в формате часы:минуты:секундыseconds = 3725
h = seconds // 3600
m = seconds % 3600 // 60
s = seconds % 60
print(f'{h}:{m}:{s}')# __SUDOTEACH_TESTS__
equals(run_variant(seconds=3725), '1:2:5', '3725 секунд')
printResults()# __SUDOTEACH_TESTS__
equals(run_variant(seconds=3725), '1:2:5', '3725 секунд')
equals(run_variant(seconds=59), '0:0:59', '59 секунд')
equals(run_variant(seconds=7200), '2:0:0', 'ровно 2 часа')
equals(run_variant(seconds=0), '0:0:0', 'ноль секунд')
printResults()Если вместо решения написать print('1:2:5'), ученик увидит:
Если тесты уже написаны через assert (Python)
Обычные assert тоже работают: на первой упавшей проверке ученик увидит строку с assert и ваше сообщение. Но есть два минуса: проверка останавливается на первой ошибке, и в конце обязательно нужен print('All tests passed') — без него задача не засчитается. Для новых задач берите equals.
assert square(3) == 9, 'square(3) должно быть 9'
assert square(-2) == 4, 'square(-2) должно быть 4'
assert square(0) == 0, 'square(0) должно быть 0'
print('All tests passed')Частые ошибки
| Ошибка | Что будет | Как правильно |
|---|---|---|
Нет printResults() (Python, C++) | Не засчитывается никогда | Последней строкой тестов |
Тесты списком пар, например [["2 3", "5"]] | Такого формата у нас нет: строка ничего не проверяет, задача не засчитывается | Проверки через equals, ввод — как в рецепте 3 |
| «Ввод (stdin)» включён у задачи с тестами | Строки, введённые учеником, попадают прямо в код: «name 'Аня' is not defined» | Выключить, ввод подменять в тестах |
| Ввод в задаче с тестами без подмены | Python: «EOF when reading a line», JavaScript: «input is not defined», C++: пустые переменные | Ввод и вывод в тестах |
C++: int main() или #include <map> в коде ученика при проверке по выводу | Ошибка компиляции | Ученик пишет только тело main() |
C++: main() у ученика в задаче с тестами | «redefinition of main» | main() — только в тестах |
| Печать перехвачена и не возвращена | Не засчитывается: итог проверок тоже перехвачен | Вернуть печать перед проверками |
Отладочная печать в поток ошибок (console.error, cerr, warnings) | Провал, даже если все проверки прошли | Печатать только в обычный вывод |
| Заготовка проходит тесты | Задачу решают, ничего не написав | Заглушки; сдайте заготовку сами |
| Точное сравнение для фраз | Ученик не проходит из-за запятой или регистра | Мягкое сравнение |
| Синтаксис новее Python 3.10 | SyntaxError у верного решения | Писать под 3.10 |
Ограничения песочницы
- Версии: Python 3.10 (в списке языков написано «Python 3.12», но код выполняется на 3.10), JavaScript — Node.js 20, C++ — GCC, стандарт C++17.
- Python: 2 секунды процессорного времени и до 100 МБ памяти на запуск, какие бы значения ни стояли в полях «Память» и «Время». JavaScript останавливается через 5 секунд. Поле «Время» (до 60 с) ограничивает общую длительность запуска, в C++ — вместе с компиляцией.
- Нет интернета. Запросы к сайтам и API не работают.
- Файлы (Python) можно создавать и читать в текущей папке, между запусками они не сохраняются.
- Сторонние библиотеки — только установленные на платформе Python-пакеты. Нужен новый — напишите в поддержку.
- Строки
importи#includeиз заготовки сами добавляются перед кодом ученика, даже если он их стёр. В C++ при проверке по выводу дополнительные заголовки (например,<map>) не подключить: код окажется внутриmain()и не скомпилируется. Нужен такой заголовок — делайте задачу с тестами, там#includeработает. - При ошибке компиляции C++ ученик видит только последнюю строку с «error:».
- В разборе показывается до 20 проверок.
Чек-лист перед публикацией
- В условии есть: что дано, имена переменных или функций, пример результата дословно.
- Заготовка запускается (в C++ — компилируется) без ошибок.
- Откройте урок на сайте и сдайте нетронутую заготовку — проверка должна упасть.
- Сдайте эталонное решение — должны пройти и «Проверить», и «Отправить».
- В полных тестах есть краевые случаи.
- Названия проверок понятны ученику.
printResults()— последней строкой и в мини-, и в полных тестах.- «Ввод (stdin)» включён, только если проверка по выводу с
{}.
Тесты с вариантами ответа
Кнопка «Добавить тест» в редакторе практики. Для каждого вопроса выберите тип: «Один» — ровно один правильный вариант, «Несколько» — правильных два и больше. Пояснение необязательно, но очень полезно: ученик увидит его после ответа.
- Проверяйте понимание, а не память: «что выведет этот код», «где ошибка», «какой вариант сработает».
- Четыре правдоподобных варианта. Без «все перечисленные» и «ни один из вариантов».
- Код в вопросе — простым текстом с переводами строк, HTML там не работает.
Всё сразу через JSON
Кнопка «JSON практики» в редакторе практики принимает тесты и задачи одним объектом. Удобно, когда задач много или вы готовите их в другом месте. Пример ниже — рабочий: квиз из двух вопросов, задача с тестами (рецепт 2) и задача с проверкой по выводу.
{
"quizzes": [
{
"title": "Проверьте себя",
"order": 1,
"tests": [
{
"title": "Что выведет print(7 // 2)?",
"test_type": "radio",
"order": 1,
"explanation": "// — целочисленное деление: дробная часть отбрасывается, получается 3.",
"test_options": [
{
"text": "3",
"is_correct": true,
"order": 1
},
{
"text": "3.5",
"is_correct": false,
"order": 2
},
{
"text": "4",
"is_correct": false,
"order": 3
},
{
"text": "1",
"is_correct": false,
"order": 4
}
]
},
{
"title": "Какие типы данных изменяемые?",
"test_type": "checkbox",
"order": 2,
"explanation": "list и dict можно менять после создания, tuple и str — нельзя.",
"test_options": [
{
"text": "list",
"is_correct": true,
"order": 1
},
{
"text": "tuple",
"is_correct": false,
"order": 2
},
{
"text": "dict",
"is_correct": true,
"order": 3
},
{
"text": "str",
"is_correct": false,
"order": 4
}
]
}
]
}
],
"tasks": [
{
"title": "Чётное или нет",
"description": "<p>Напишите функцию <code>is_even(n)</code>, которая возвращает <code>True</code> для чётного числа и <code>False</code> для нечётного.</p>",
"order": 1,
"stack": "python312",
"placeholder": "def is_even(n):\n # верните True, если n чётное\n pass\n",
"no_test": false,
"mini_tests": "equals(is_even(4), True, \"is_even(4)\")\nequals(is_even(7), False, \"is_even(7)\")\nprintResults()",
"full_tests": "equals(is_even(4), True, \"is_even(4)\")\nequals(is_even(7), False, \"is_even(7)\")\nequals(is_even(0), True, \"ноль — чётное число\")\nequals(is_even(-3), False, \"отрицательное нечётное\")\nequals(is_even(-10), True, \"отрицательное чётное\")\nprintResults()",
"right_answer": null,
"output_check": "exact",
"memory_limit": 256,
"time_limit": 18,
"input_required": false,
"ai_enabled": false,
"prompt": null,
"is_premium": false
},
{
"title": "Привет, мир",
"description": "<p>Выведите фразу <code>Hello, World!</code></p>",
"order": 2,
"stack": "python312",
"placeholder": "# напишите программу здесь\n",
"no_test": true,
"mini_tests": null,
"full_tests": null,
"right_answer": "Hello, World!",
"output_check": "lenient",
"memory_limit": 256,
"time_limit": 18,
"input_required": false,
"ai_enabled": false,
"prompt": null,
"is_premium": false
}
]
}| Поле задачи | Что значит |
|---|---|
stack | Язык: python312, javascript, typescript, java, go, cpp, csharp, rust, swift, sql |
no_test | true — проверка по выводу, false — через тесты |
mini_tests, full_tests | Код тестов одной строкой, переводы строк — \n. При проверке по выводу — null |
right_answer | Ожидаемый вывод (только при no_test: true, иначе null) |
output_check | exact — точное, lenient — мягкое, run — самопроверка |
input_required | Поле «Ввод (stdin)». У задач с тестами — всегда false |
time_limit, memory_limit | Секунды (до 60) и мегабайты (до 1024) |
Другие языки и SQL
equals и printResults есть и в TypeScript, Java, Go, C#, Rust и Swift: тесты пишутся так же — проверки, затем итог последней строкой. Разбор у ученика выглядит одинаково.
У SQL-задач свои поля. В «Seed SQL» создайте таблицы и наполните их данными, в «Эталонный SQL-запрос» впишите правильное решение. Результат запроса ученика сравнивается с результатом эталона. Включите «Порядок строк важен», если в задаче нужен ORDER BY.