/
marsela13
/
practic
Обзор
Документация
Войти
/
marsela13
/
practic
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tests/test_integration_basic.py
493 строки
20 KB
marsela13
Upload files to "tests"
25 дек 2025, 15:34
25 дек 2025, 15:34
00b2246
Код
Авторство
О чём код?
""" Базовые интеграционные тесты проекта Network Scanner GUI. Тестирует взаимодействие компонентов системы в контролируемой среде. """ import sys import os import tempfile import json from pathlib import Path from unittest.mock import Mock, patch, MagicMock import pytest # Добавление пути к проекту sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from network_scanner import NetworkScanner from scanner_gui import ScanThread, NetworkScannerGUI class TestNetworkScannerIntegration: """Интеграционные тесты NetworkScanner.""" def test_complete_scan_workflow(self, temp_dir): """Тест полного рабочего процесса сканирования.""" scanner = NetworkScanner(max_threads=10, timeout=0.5) # Мокаем все сетевые операции with patch.object(scanner, 'ping_host') as mock_ping, \ patch.object(scanner, 'get_hostname') as mock_hostname, \ patch.object(scanner, 'scan_port') as mock_scan_port: # Настраиваем моки mock_ping.return_value = True mock_hostname.return_value = 'test.local' mock_scan_port.return_value = True # Парсим диапазон ip_list = scanner.parse_ip_range('192.168.1.1-3') assert len(ip_list) == 3 # Выполняем сканирование results = scanner.scan_network('192.168.1.1-3', [80, 443]) # Проверяем результаты assert len(results) == 3 for result in results: assert result['status'] == 'Online' assert result['open_ports'] == [80, 443] # Экспортируем результаты json_file = Path(temp_dir) / 'results.json' csv_file = Path(temp_dir) / 'results.csv' json_result = scanner.export_to_json(str(json_file)) csv_result = scanner.export_to_csv(str(csv_file)) assert json_result is True assert csv_result is True assert json_file.exists() assert csv_file.exists() def test_scan_with_progress_callback(self): """Тест сканирования с callback прогресса.""" scanner = NetworkScanner(max_threads=5, timeout=0.1) # Создаем mock callback mock_callback = Mock() scanner.set_progress_callback(mock_callback) # Мокаем сетевые операции with patch.object(scanner, 'ping_host', return_value=True), \ patch.object(scanner, 'get_hostname', return_value='test.local'), \ patch.object(scanner, 'scan_port', return_value=True): # Выполняем сканирование небольшого диапазона results = scanner.scan_network('192.168.1.1-2', [80]) # Проверяем, что callback вызывался # (точное количество вызовов зависит от реализации) assert mock_callback.called or len(results) > 0 def test_cancel_scan_integration(self): """Тест отмены сканирования в процессе работы.""" scanner = NetworkScanner(max_threads=10, timeout=0.5) # Мокаем длительную операцию def slow_ping(ip): import time time.sleep(0.1) # Имитируем задержку return True with patch.object(scanner, 'ping_host', side_effect=slow_ping): # Запускаем сканирование в отдельном потоке import threading def scan_thread(): scanner.scan_network('192.168.1.1-10', [80]) thread = threading.Thread(target=scan_thread) thread.start() # Даем немного времени на запуск import time time.sleep(0.05) # Отменяем сканирование scanner.cancel_scan() # Ждем завершения потока thread.join(timeout=1.0) # Проверяем, что флаг установлен assert scanner.is_cancelled is True def test_export_import_workflow(self, temp_dir): """Тест полного цикла экспорт-импорт.""" scanner = NetworkScanner() # Создаем тестовые данные test_data = [ { 'ip': '192.168.1.1', 'hostname': 'router', 'status': 'Online', 'open_ports': [80, 443], 'timestamp': '2024-01-15T10:00:00.000000' } ] scanner.results = test_data # Экспортируем в JSON json_file = Path(temp_dir) / 'export.json' scanner.export_to_json(str(json_file)) # Читаем обратно with open(json_file, 'r', encoding='utf-8') as f: imported_data = json.load(f) # Проверяем, что данные совпадают assert imported_data == test_data # Тестируем CSV csv_file = Path(temp_dir) / 'export.csv' scanner.export_to_csv(str(csv_file)) assert csv_file.exists() class TestScanThreadIntegration: """Интеграционные тесты ScanThread.""" def test_scan_thread_creation(self, mock_scanner): """Тест создания потока сканирования.""" thread = ScanThread(mock_scanner, '192.168.1.1-10', [80, 443]) assert thread.scanner == mock_scanner assert thread.ip_range == '192.168.1.1-10' assert thread.ports == [80, 443] assert thread.isRunning() is False def test_scan_thread_signals(self, qapplication, mock_scanner): """Тест сигналов потока сканирования.""" thread = ScanThread(mock_scanner, '192.168.1.1-3', [80]) # Создаем mock обработчиков сигналов mock_progress_handler = Mock() mock_finished_handler = Mock() mock_error_handler = Mock() # Подключаем обработчики thread.scan_progress.connect(mock_progress_handler) thread.scan_finished.connect(mock_finished_handler) thread.scan_error.connect(mock_error_handler) # Настраиваем мок сканера test_results = [{'ip': '192.168.1.1', 'status': 'Online'}] mock_scanner.scan_network.return_value = test_results # Запускаем поток thread.start() thread.wait(1000) # Ждем завершения # Проверяем вызовы mock_finished_handler.assert_called_once_with(test_results) mock_progress_handler.assert_called() # Должен вызываться минимум один раз mock_error_handler.assert_not_called() def test_scan_thread_error_handling(self, qapplication, mock_scanner): """Тест обработки ошибок в потоке сканирования.""" thread = ScanThread(mock_scanner, '192.168.1.1-3', [80]) # Создаем mock обработчика ошибок mock_error_handler = Mock() thread.scan_error.connect(mock_error_handler) # Настраиваем мок для выброса исключения mock_scanner.scan_network.side_effect = Exception("Test error") # Запускаем поток thread.start() thread.wait(1000) # Проверяем, что ошибка была передана mock_error_handler.assert_called_once_with("Test error") def test_scan_thread_progress_updates(self, qapplication, mock_scanner): """Тест обновлений прогресса в потоке сканирования.""" thread = ScanThread(mock_scanner, '192.168.1.1-3', [80]) # Создаем mock обработчика прогресса mock_progress_handler = Mock() thread.scan_progress.connect(mock_progress_handler) # Настраиваем callback в сканере progress_callback = None def capture_callback(callback): nonlocal progress_callback progress_callback = callback mock_scanner.set_progress_callback.side_effect = capture_callback # Запускаем поток thread.start() # Ждем немного чтобы callback был установлен import time time.sleep(0.1) # Вызываем callback (имитируем прогресс сканирования) if progress_callback: test_result = {'ip': '192.168.1.1', 'status': 'Online'} progress_callback(1, 3, 1, test_result) # Проверяем, что сигнал был излучен mock_progress_handler.assert_called_once_with(1, 3, 1, test_result) thread.wait(500) class TestGUIComponentsIntegration: """Интеграционные тесты GUI компонентов.""" @patch('PyQt5.QtWidgets.QApplication') def test_gui_initialization(self, mock_qapp): """Тест инициализации GUI.""" # Создаем mock QApplication mock_app = Mock() mock_qapp.instance.return_value = None mock_qapp.return_value = mock_app # Создаем GUI gui = NetworkScannerGUI() # Проверяем основные атрибуты assert gui.scanner is not None assert isinstance(gui.scanner, NetworkScanner) assert gui.scan_thread is None # Проверяем, что методы инициализации были вызваны # (не можем проверить напрямую, так как они вызываются в конструкторе) def test_gui_settings_panel_creation(self, qapplication): """Тест создания панели настроек в GUI.""" gui = NetworkScannerGUI() # Проверяем, что основные элементы созданы assert hasattr(gui, 'ip_input') assert hasattr(gui, 'ports_input') assert hasattr(gui, 'common_ports_check') assert hasattr(gui, 'threads_spin') assert hasattr(gui, 'timeout_spin') assert hasattr(gui, 'scan_button') assert hasattr(gui, 'stop_button') assert hasattr(gui, 'export_button') assert hasattr(gui, 'clear_button') # Проверяем начальные значения assert gui.ip_input.text() == '192.168.1.1-100' assert gui.ports_input.text() == '22,80,443,3389' assert gui.common_ports_check.isChecked() is True assert gui.threads_spin.value() == 50 assert gui.timeout_spin.value() == 1 def test_gui_results_table_creation(self, qapplication): """Тест создания таблицы результатов в GUI.""" gui = NetworkScannerGUI() # Проверяем таблицу assert hasattr(gui, 'results_table') assert gui.results_table.columnCount() == 5 # Проверяем заголовки headers = [] for i in range(gui.results_table.columnCount()): headers.append(gui.results_table.horizontalHeaderItem(i).text()) expected_headers = ["IP", "Hostname", "Status", "Открытые порты", "Время обнаружения"] assert headers == expected_headers @patch.object(NetworkScannerGUI, 'start_scan') def test_gui_button_connections(self, mock_start_scan, qapplication): """Тест подключений кнопок в GUI.""" gui = NetworkScannerGUI() # Симулируем нажатие кнопки сканирования gui.scan_button.click() # Проверяем, что метод start_scan был вызван mock_start_scan.assert_called_once() def test_gui_log_functionality(self, qapplication): """Тест функциональности лога в GUI.""" gui = NetworkScannerGUI() # Проверяем начальное состояние assert gui.log_text.toPlainText() == '' # Добавляем сообщение в лог test_message = "Test log message" gui.log_message(test_message) # Проверяем, что сообщение добавлено log_content = gui.log_text.toPlainText() assert test_message in log_content assert "[" in log_content # Должна быть временная метка def test_gui_progress_bar(self, qapplication): """Тест работы прогресс-бара в GUI.""" gui = NetworkScannerGUI() # Проверяем начальное значение assert gui.progress_bar.value() == 0 # Устанавливаем значение gui.progress_bar.setValue(50) assert gui.progress_bar.value() == 50 # Устанавливаем 100% gui.progress_bar.setValue(100) assert gui.progress_bar.value() == 100 class TestConfigurationIntegration: """Интеграционные тесты конфигурации.""" def test_settings_save_load_cycle(self, temp_dir): """Тест цикла сохранения и загрузки настроек.""" # Создаем временный файл настроек settings_file = Path(temp_dir) / 'test_settings.json' # Тестовые настройки test_settings = { 'ip_range': '10.0.0.1-50', 'ports': '22,80,443,8080', 'scan_common_ports': False, 'max_threads': 100, 'timeout': 2 } # Сохраняем настройки with open(settings_file, 'w') as f: json.dump(test_settings, f, indent=2) # Загружаем настройки with open(settings_file, 'r') as f: loaded_settings = json.load(f) # Проверяем, что настройки совпадают assert loaded_settings == test_settings @patch('os.path.exists') @patch('builtins.open') def test_gui_settings_load(self, mock_open, mock_exists, qapplication): """Тест загрузки настроек в GUI.""" mock_exists.return_value = True # Тестовые настройки test_settings = { 'ip_range': '172.16.0.1-100', 'ports': '21,22,23', 'scan_common_ports': False, 'max_threads': 75, 'timeout': 3 } # Мокаем чтение файла mock_file = Mock() mock_file.__enter__.return_value.read.return_value = json.dumps(test_settings) mock_open.return_value = mock_file # Создаем GUI (он загрузит настройки автоматически) gui = NetworkScannerGUI() # Проверяем, что файл пытались открыть mock_open.assert_called_with('scanner_settings.json', 'r') def test_gui_settings_save(self, qapplication, temp_dir): """Тест сохранения настроек из GUI.""" gui = NetworkScannerGUI() # Изменяем настройки gui.ip_input.setText('192.168.0.1-200') gui.ports_input.setText('22,3389') gui.common_ports_check.setChecked(False) gui.threads_spin.setValue(25) gui.timeout_spin.setValue(5) # Мокаем открытие файла для записи saved_data = None def mock_write(data): nonlocal saved_data saved_data = json.loads(data) mock_file = Mock() mock_file.__enter__.return_value.write = mock_write mock_file.__enter__.return_value.__exit__ = Mock() with patch('builtins.open', return_value=mock_file): gui.save_settings() # Проверяем сохраненные данные assert saved_data is not None assert saved_data['ip_range'] == '192.168.0.1-200' assert saved_data['ports'] == '22,3389' assert saved_data['scan_common_ports'] is False assert saved_data['max_threads'] == 25 assert saved_data['timeout'] == 5 class TestExportImportIntegration: """Интеграционные тесты экспорта и импорта.""" def test_export_results_from_gui(self, qapplication, temp_dir): """Тест экспорта результатов из GUI.""" gui = NetworkScannerGUI() # Устанавливаем тестовые результаты test_results = [ { 'ip': '192.168.1.1', 'hostname': 'test1', 'status': 'Online', 'open_ports': [80, 443], 'timestamp': '2024-01-15T10:00:00.000000' } ] gui.scanner.results = test_results # Мокаем диалог выбора файла test_file = str(Path(temp_dir) / 'gui_export.json') with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName') as mock_dialog: mock_dialog.return_value = (test_file, 'JSON files (*.json)') # Мокаем экспорт with patch.object(gui.scanner, 'export_to_json') as mock_export: mock_export.return_value = True gui.export_results() # Проверяем вызов mock_export.assert_called_once_with(test_file) def test_clear_results_from_gui(self, qapplication): """Тест очистки результатов из GUI.""" gui = NetworkScannerGUI() # Устанавливаем тестовые результаты gui.scanner.results = [{'ip': '192.168.1.1', 'status': 'Online'}] gui.results_table.setRowCount(1) gui.progress_bar.setValue(50) gui.export_button.setEnabled(True) # Мокаем диалог подтверждения with patch('PyQt5.QtWidgets.QMessageBox.question') as mock_question: mock_question.return_value = gui.QMessageBox.Yes gui.clear_results() # Проверяем очистку assert gui.scanner.results == [] assert gui.results_table.rowCount() == 0 assert gui.progress_bar.value() == 0 assert gui.export_button.isEnabled() is False if __name__ == '__main__': pytest.main([__file__, '-v'])