Files
English/kouyu_english/test/lesson_options_test.dart
T

147 lines
5.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter_test/flutter_test.dart';
import 'package:kouyu_english/core/courses/courses.dart';
const _digitWords = {
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
'5': 'five',
'6': 'six',
'7': 'seven',
'8': 'eight',
'9': 'nine',
};
const _skipWords = {'a', 'an', 'the', 'my', 'is', 'it', 'im', 'to', 'you'};
String _normalize(String text) =>
text.toLowerCase().replaceAll(RegExp(r'[^a-z0-9一-龥]'), '');
/// 选项在对话里是否真的出现过。数字选项按英文读法展开(138 → onethreeeight),
/// 逐字母拼写(S-H-E-N)先去掉连字符。
bool _appearsInReading(String reading, String option) {
final haystack = _normalize(
reading.replaceAllMapped(RegExp(r'\d'), (match) => _digitWords[match[0]]!),
);
final tokens = option
.replaceAll('-', '')
.replaceAllMapped(RegExp(r'\d'), (match) => _digitWords[match[0]]!)
.toLowerCase()
.split(RegExp(r'[^a-z0-9一-龥]+'))
.where((token) => token.length > 1 && !_skipWords.contains(token));
return tokens.isNotEmpty && tokens.every(haystack.contains);
}
void main() {
final activities = segmentActivities;
group('阅读找答案题', () {
test('每题三个选项,第一项是标准答案', () {
for (final entry in activities.entries) {
final activity = entry.value;
expect(activity.answers, hasLength(3), reason: entry.key);
expect(activity.readingOptions, hasLength(3), reason: entry.key);
expect(
activity.readingOptions.first,
activity.readingAnswer,
reason: '${entry.key}readingOptions 第一项应是标准答案',
);
expect(
activity.readingOptions.toSet(),
hasLength(3),
reason: '${entry.key}:选项不能重复',
);
}
});
test('至少两个选项在对话中出现,必须读懂才能选', () {
for (final entry in activities.entries) {
final activity = entry.value;
final present = activity.readingOptions
.where((option) => _appearsInReading(activity.reading, option))
.toList();
expect(
_appearsInReading(activity.reading, activity.readingAnswer),
isTrue,
reason: '${entry.key}:答案必须能在对话里找到',
);
expect(
present.length,
greaterThanOrEqualTo(2),
reason:
'${entry.key}:只有 ${present.length} 个选项出现在对话中,'
'学习者不读对话也能挑出唯一出现过的那个',
);
}
});
test('选项之间不构成子串,避免判分时误判', () {
for (final entry in activities.entries) {
final options = entry.value.readingOptions.map(_normalize).toList();
for (var i = 0; i < options.length; i++) {
for (var j = i + 1; j < options.length; j++) {
expect(
options[i].contains(options[j]) ||
options[j].contains(options[i]),
isFalse,
reason:
'${entry.key}:“${entry.value.readingOptions[i]}”与'
'“${entry.value.readingOptions[j]}”互为子串,会被判分逻辑当成同一个答案',
);
}
}
}
});
test('打乱后的选项内容不变且顺序稳定', () {
for (final entry in activities.entries) {
final activity = entry.value;
final seed = '${activity.readingQuestion}-reading';
final first = shuffledOptions(activity.readingOptions, seed);
final second = shuffledOptions(activity.readingOptions, seed);
expect(first, second, reason: entry.key);
expect(
first.toSet(),
activity.readingOptions.toSet(),
reason: entry.key,
);
}
});
test('正确答案在三个位置上都出现,不集中在某一位', () {
final readingSlots = <int, int>{};
final listeningSlots = <int, int>{};
for (final activity in activities.values) {
final reading = shuffledOptions(
activity.readingOptions,
'${activity.readingQuestion}-reading',
);
final readingSlot = reading.indexOf(activity.readingAnswer);
readingSlots[readingSlot] = (readingSlots[readingSlot] ?? 0) + 1;
final listening = shuffledOptions(
activity.answers,
'${activity.listening}-listening',
);
final listeningSlot = listening.indexOf(activity.answers.first);
listeningSlots[listeningSlot] =
(listeningSlots[listeningSlot] ?? 0) + 1;
}
for (final slots in [readingSlots, listeningSlots]) {
expect(slots.keys.toSet(), {0, 1, 2});
for (final count in slots.values) {
expect(count, lessThan(activities.length ~/ 2));
}
}
});
test('题干不重复,复习时不会撞题', () {
final questions = activities.values
.map((activity) => activity.readingQuestion)
.toList();
expect(questions.toSet(), hasLength(questions.length));
});
});
}