Я провожу модульное тестирование программы проверки кредитной карты, которая принимает файл .txt, содержащий поддельные номера кредитных карт, и возвращает словарь карты, действующий ли это номер кредитной карты, и тип карты (visa, mastercard и т. д.).
Используя модуль unittest, я успешно установил, что моя реализация алгоритма Луна надежна и что программа может правильно идентифицировать тип карты, используя набор номеров карт, жестко закодированных в тестовом файле. Однако при попытке прочитать файл .txt в программу и проверить, что он возвращает правильные результаты, вместо этого выводится None.
Это какая-то особенность модуля unittest, когда он не может сравнивать выходные данные функции, когда сама функция читает файлы?
Кроме того, файл .txt находится в той же общей папке, что и файл функции и тестовый файл.
Функция обработки файла
def process_file(file_path):
try:
with open(file_path, 'r') as file:
card_numbers = file.readlines()
results = {}
for card_number in card_numbers:
is_valid = check_card_number(card_number)
card_type = get_card_type(card_number)
results[card_number] = (is_valid, card_type)
return results
except Exception as e:
print(f"Error reading file: {e}")
def check_card_number(card_number):
if not card_number.isdigit():
return False
return luhn_algorithm(card_number)
def get_card_type(card_number):
patterns = {
'Visa': r"^4[0-9]{12}(?:[0-9]{3})?$",
'MasterCard': r"^5[1-5][0-9]{14}$",
'American Express': r"^3[47][0-9]{13}$",
'Discover': r"^6(?:011|5[0-9]{2})[0-9]{12}$",
'JCB': r"^(?:2131|1800|35\d{3})\d{11}$",
'Diners Club': r"^3(?:0[0-5]|[68][0-9])[0-9]{11}$",
'Maestro': r"^(5018|5020|5038|56|57|58|6304|6759|676[1-3])\d{8,15}$"
}
for type, pattern in patterns.items():
if re.match(pattern, card_number):
return type
return 'Unknown'
def luhn_algorithm(card_number:str) -> bool:
payload = [int(d) for d in card_number[:-1]]
even, odd = payload[-1::-2], payload[-2::-2]
total = sum(odd) + sum([(i * 2) - 9 if i >= 5 else i * 2 for i in even])
return 10 - (total % 10) == int(card_number[-1])
Функция файла процесса тестирования
class TestProcessFile(unittest.TestCase):
def test_validate_card_and_get_type(self):
file_path = r'file_path\test_cards.txt'
credit_cards = ('6390448951544', '3507088898930091')
correct_card_types = ('Unknown', 'JCB')
correct_validation = (False, True)
correct_results = dict(zip(credit_cards, zip(correct_validation, correct_card_types)))
self.assertEqual(process_file(file_path), correct_results)
При запуске самой программы она прекрасно читает файл и генерирует ожидаемый результат. Но при тестировании программы с помощью unittest в методе AssertEqual функция выводит None вместо ожидаемого словаря.
@AndrewYim, предоставленная программа была минимально сокращена, но при этом воспроизводима. Входной файл .txt представляет собой просто числа (предназначенные для имитации номеров кредитных карт, но на самом деле это могут быть любые числа), каждое из которых находится на новой строке.
Потенциальный обман stackoverflow.com/questions/55695776/…?
Потенциально это может быть так, @AndrewYim. При этом тестовый файл, основные файлы программы и файл .txt находятся в одной папке и имеют общий каталог. Так что я немного сомневаюсь в этом. Но я рассмотрю это. Спасибо
@Camocarrot Вы уверены, что при выполнении test_validate_card_and_get_type()
переменная file_path
содержит правильный путь к файлу test_cards.txt
? Я протестировал ваш код в своей системе, и выполнение process_file()
вызывает (очевидно) исключение Error reading file:
, поэтому оно возвращается None
...
Причина в том, что вы не определили функции «check_card_number» и «get_card_type». Таким образом, ваш набор тестов возвращает None, поскольку выполнение неполное. Попробуйте определить эти две функции, и тогда результаты должны совпасть. Пример:
def check_card_number(n):
return True
def get_card_type(c):
"x"
Привет, спасибо за ваш ответ. Что касается вашего предложения, то две функции были определены, но они были исключены из моего объяснения, поскольку тестирование этих двух функций уже оказалось успешным.
Поскольку фактический запуск файла main.py дает ожидаемые результаты, я уверен, что функцииprocess_file работают. Я просто пытаюсь продемонстрировать это с помощью unittest
Я выполнил ваш код в своей системе с двумя изменениями.
Я добавил в файл следующий импорт process.py
:
import re
Я создал текстовый файл test_cards.txt
в той же папке process.py
со следующим содержимым:
6390448951544
3507088898930091
В той же папке я создал тестовый файл:
from process import process_file
import unittest
class TestProcessFile(unittest.TestCase):
def test_validate_card_and_get_type(self):
#file_path = r'file_path\test_cards.txt'
file_path = r'./test_cards.txt'
credit_cards = ('6390448951544\n', '3507088898930091')
correct_card_types = ('Unknown', 'JCB')
correct_validation = (False, True)
correct_results = dict(zip(credit_cards, zip(correct_validation, correct_card_types)))
self.assertEqual(process_file(file_path), correct_results)
if __name__ == '__main__':
unittest.main()
Предыдущий тестовый файл идентичен вашему файлу, но со вторым моим изменением: определение переменной credit_cards
имеет следующую модификацию (на мой взгляд, это не важно и связано с тем фактом, что я использую систему Linux, в то время как я думаю, что вы используете система Windows):
# I HAVE ADDED THE char '\n' in the first credit card number
credit_cards = ('6390448951544\n', '3507088898930091')
В моей системе выполнение test_process.py
дает следующий успешный результат (process_file()
не возвращает None
):
> python test_process.py
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Большое спасибо за помощь, @User051209. Изменение пути к файлу на r".\test_cards_test.txt" в тестовом файле помогло мне.
Можете ли вы свести эту программу к минимально воспроизводимому примеру? Как вы сказали, большинство функций уже работают нормально; ваша проблема станет намного яснее, если вы сможете минимизировать количество кода в своем вопросе.