Використання регресійних тестів

Last updated on 2026-04-28 | Edit this page

Estimated time: 50 minutes

Overview

Questions

  • Тестова розробка

Objectives

  • Вміти створювати та запускати тестові файли
  • Зрозуміти, як розбіжності тестів і регресії під час виконання можна ідентифікувати та інтерпретувати
  • Зрозуміти, як налаштувати тести для перевірки рандомізованих алгоритмів
  • Вивчити концепцію ’Зробіть спочатку правильно, а потім швидко

Код AvgOrdOfGroup дуже простий, і з ним нічого не може піти не так. Перебираючи групу замість створення списку її елементів, це дозволяє уникнути нестачі пам’яті (виклик AsList(SymmetricGroup(11)) вже призводить до перевищення дозволеної пам’яті). Тим не менш, обчислення все одно займає час, кілька хвилин необхідні для розрахунку середнього порядку елемента SymmetricGroup(11). Але принаймні ми впевнені, що це правильно.

Тепер ми хотіли б написати кращу версію цієї функції, використовуючи деякі теоретичні факти, відомі нам із теорії груп. Ми можемо поставити avgord.g під контроль версій, щоб скасувати зміни, якщо потрібно; ми можемо створити нову функцію, щоб зберегти стару версію й порівняти результати обох; але це можна зробити ще ефективнішим, якщо ми використаємо regression testing: це термін для тестування на основі повторних запусків раніше завершених тестів, щоб переконатися, що нові зміни не вплинуть на їх коректність або не погіршать їх роботу.

Для початку нам потрібно створити тестовий файл. Тестовий файл виглядає точно так само, як сеанс GAP, тому його легко створити, скопіювавши та вставивши сеанс GAP з усіма підказками, входами та виходами GAP у текстовий файл (тестовий файл також можна створити з файлу журналу із сеансом GAP, записаним за допомогою LogTo). Під час тестування GAP виконуватиме всі вхідні дані з тестового файлу, порівнюватиме результати з тими, що містяться в тестовому файлі, і повідомлятиме про будь-які відмінності.

Тестові файли GAP — це просто текстові файли, але загальноприйнятою практикою є назвати їх із розширенням .tst. Тепер створіть файл avgord.tst у поточному каталозі (щоб вводити повний шлях) з наступним вмістом:

GAP

# tests for average order of a group element

# permutation group
gap> S:=SymmetricGroup(9);
Sym( [ 1 .. 9 ] )
gap> AvgOrdOfGroup(S);
3291487/362880

Як ви бачите, тестовий файл може включати коментарі з певними правилами, що визначають, де їх можна розміщувати, тому що потрібно мати можливість відрізнити коментарі в тестовому файлі від вихідних даних GAP, які починаються з #. З цією метою, рядки на початку тестового файлу, які починаються з #, і один порожній рядок разом з одним або декількома рядками, що починаються з #, вважаються коментарями. Усі інші рядки інтерпретуються як вихід GAP із попереднього введення GAP.

Щоб запустити тест, слід використовувати функцію Test, як описано в документації тут. Наприклад (за умови, що функція AvgOrdOfGroup вже завантажена):

GAP

Test("avgord.tst");

OUTPUT

true

У цьому випадку Test не повідомив про відсутність розбіжностей і повернув true, тому ми робимо висновок, що перевірку пройдено.

Ми не розглядатимемо тут тему написання гарного та комплексного набору тестів, а також не будемо розглядати різні параметри функції Test, що дозволяє нам, наприклад, ігнорувати відмінності у форматуванні виводу або відображати хід виконання тесту, як це описано в його документації.

Замість цього ми додамо більше груп до avgord.tst, щоб продемонструвати, що код також працює з іншими типами груп, і показати різні способи поєднання команд у тестовому файлі:

GAP

# tests for average order of a group element

# permutation group
gap> S:=SymmetricGroup(9);
Sym( [ 1 .. 9 ] )
gap> AvgOrdOfGroup(S);
3291487/362880

# pc group
gap> D:=DihedralGroup(512);

gap> AvgOrdOfGroup(D);
44203/512
gap> G:=TrivialGroup();; # suppress output
gap> AvgOrdOfGroup(G);
1

# fp group
gap> F:=FreeGroup("a","b");

gap> G:=F/ParseRelators(GeneratorsOfGroup(F),"a^8=b^2=1, b^-1ab=a^-1");

gap> IsFinite(G);
true
gap> AvgOrdOfGroup(G);
59/16

# finite matrix group over integers
gap> AvgOrdOfGroup( Group( [[0,-1],[1,0]] ) );
11/4

# matrix group over a finite field
gap> AvgOrdOfGroup(SL(2,5));
221/40

Давайте ще раз протестуємо розширену версію тесту та перевіримо, чи вона працює:

GAP

Test("avgord.tst");

OUTPUT

true

Зараз ми будемо працювати над кращою реалізацією. Звичайно, порядок елемента є інваріантом класу спряженості елементів групи, тому нам потрібно лише знати порядки класів спряженості елементів та їх представників. Наступний код, який ми додаємо до avgord.g, зчитує GAP і перевизначає AvgOrdOfGroup без синтаксичних помилок:

GAP

AvgOrdOfGroup := function(G)
local cc, sum, c;
cc:=ConjugacyClasses(G);
sum:=0;
for c in cc do
  sum := sum + Order( Representative(c) ) * Size(cc);
od;
return sum/Size(G);
end;

але коли ми запускаємо тест, ось сюрприз!

GAP

Read("avgord.g");
Test("avgord.tst");

OUTPUT

########> Diff in avgord.tst, line 6:
# Input is:
AvgOrdOfGroup(S);
# Expected output:
3291487/362880
# But found:
11/672
########
########> Diff in avgord.tst, line 12:
# Input is:
AvgOrdOfGroup(D);
# Expected output:
44203/512
# But found:
2862481/512
########
########> Diff in avgord.tst, line 23:
# Input is:
AvgOrdOfGroup(G);
# Expected output:
59/16
# But found:
189/16
########
########> Diff in avgord.tst, line 29:
# Input is:
AvgOrdOfGroup(SL(2,5));
# Expected output:
221/40
# But found:
69/20
########
false

Дійсно, ми допустили помилку (навмисно) і замінили Size(c) на Size(cc). Правильний варіант курсу має виглядати так:

GAP

AvgOrdOfGroup := function(G)
local cc, sum, c;
cc:=ConjugacyClasses(G);
sum:=0;
for c in cc do
  sum := sum + Order( Representative(c) ) * Size(c);
od;
return sum/Size(G);
end;

Тепер ми виправимо це в avgord.g, прочитаємо та протестуємо його знову, щоб перевірити, чи тести виконуються правильно.

GAP

Read("avgord.g");
Test("avgord.tst");

OUTPUT

true

Таким чином, підхід «Зробіть спочатку правильно, а потім швидко» допоміг виявити помилку відразу після її появи.

Key Points
  • Легко створити тестовий файл, скопіювавши та вставивши сеанс GAP.
  • Написання хорошого та комплексного набору тестів вимагає певних зусиль.
  • Зробіть спочатку правильно, а потім швидко!