Проблемы с чтением файла при использовании unittest в Python

Я провожу модульное тестирование программы проверки кредитной карты, которая принимает файл .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 вместо ожидаемого словаря.

Можете ли вы свести эту программу к минимально воспроизводимому примеру? Как вы сказали, большинство функций уже работают нормально; ваша проблема станет намного яснее, если вы сможете минимизировать количество кода в своем вопросе.

Andrew Yim 21.07.2024 17:50

@AndrewYim, предоставленная программа была минимально сокращена, но при этом воспроизводима. Входной файл .txt представляет собой просто числа (предназначенные для имитации номеров кредитных карт, но на самом деле это могут быть любые числа), каждое из которых находится на новой строке.

Camocarrot 21.07.2024 18:02

Потенциальный обман stackoverflow.com/questions/55695776/…?

Andrew Yim 21.07.2024 18:04

Потенциально это может быть так, @AndrewYim. При этом тестовый файл, основные файлы программы и файл .txt находятся в одной папке и имеют общий каталог. Так что я немного сомневаюсь в этом. Но я рассмотрю это. Спасибо

Camocarrot 21.07.2024 18:08

@Camocarrot Вы уверены, что при выполнении test_validate_card_and_get_type() переменная file_path содержит правильный путь к файлу test_cards.txt? Я протестировал ваш код в своей системе, и выполнение process_file() вызывает (очевидно) исключение Error reading file:, поэтому оно возвращается None...

User051209 22.07.2024 10:28
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
5
55
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Причина в том, что вы не определили функции «check_card_number» и «get_card_type». Таким образом, ваш набор тестов возвращает None, поскольку выполнение неполное. Попробуйте определить эти две функции, и тогда результаты должны совпасть. Пример:

def check_card_number(n):
  return True
def get_card_type(c):
  "x"

Привет, спасибо за ваш ответ. Что касается вашего предложения, то две функции были определены, но они были исключены из моего объяснения, поскольку тестирование этих двух функций уже оказалось успешным.

Camocarrot 21.07.2024 17:39

Поскольку фактический запуск файла main.py дает ожидаемые результаты, я уверен, что функцииprocess_file работают. Я просто пытаюсь продемонстрировать это с помощью unittest

Camocarrot 21.07.2024 17:41
Ответ принят как подходящий

Я выполнил ваш код в своей системе с двумя изменениями.

Я добавил в файл следующий импорт 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" в тестовом файле помогло мне.

Camocarrot 22.07.2024 18:13

Другие вопросы по теме