Files
hospital_performance/backend/tests/test_survey_service.py
2026-02-28 15:06:52 +08:00

280 lines
9.3 KiB
Python

"""
满意度调查服务测试用例
测试覆盖:
1. 问卷管理功能
2. 题目管理功能
3. 回答提交功能
4. 统计分析功能
"""
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from datetime import datetime
from app.services.survey_service import SurveyService
from app.models.models import (
Survey, SurveyQuestion, SurveyResponse, SurveyAnswer,
SurveyStatus, SurveyType, QuestionType
)
class TestSurveyManagement:
"""问卷管理测试"""
@pytest.mark.asyncio
async def test_create_survey(self):
"""测试创建问卷"""
mock_db = AsyncMock()
# 模拟数据库操作
survey = MagicMock()
survey.id = 1
survey.survey_name = "测试问卷"
survey.survey_code = "TEST_001"
survey.survey_type = SurveyType.INPATIENT
survey.status = SurveyStatus.DRAFT
with patch.object(SurveyService, 'get_survey_by_id', return_value=None):
with patch.object(SurveyService, 'get_survey_list', return_value=([], 0)):
result = await SurveyService.create_survey(
mock_db,
survey_name="测试问卷",
survey_code="TEST_001",
survey_type=SurveyType.INPATIENT
)
mock_db.add.assert_called_once()
@pytest.mark.asyncio
async def test_publish_survey(self):
"""测试发布问卷"""
mock_db = AsyncMock()
# 创建模拟问卷
survey = MagicMock()
survey.id = 1
survey.status = SurveyStatus.DRAFT
survey.total_questions = 5
with patch.object(SurveyService, 'get_survey_by_id', return_value=survey):
result = await SurveyService.publish_survey(mock_db, 1)
assert survey.status == SurveyStatus.PUBLISHED
mock_db.flush.assert_called()
@pytest.mark.asyncio
async def test_publish_survey_without_questions(self):
"""测试发布无题目的问卷(应失败)"""
mock_db = AsyncMock()
survey = MagicMock()
survey.id = 1
survey.status = SurveyStatus.DRAFT
survey.total_questions = 0
with patch.object(SurveyService, 'get_survey_by_id', return_value=survey):
with pytest.raises(ValueError, match="问卷没有题目"):
await SurveyService.publish_survey(mock_db, 1)
@pytest.mark.asyncio
async def test_close_survey(self):
"""测试结束问卷"""
mock_db = AsyncMock()
survey = MagicMock()
survey.id = 1
survey.status = SurveyStatus.PUBLISHED
with patch.object(SurveyService, 'get_survey_by_id', return_value=survey):
result = await SurveyService.close_survey(mock_db, 1)
assert survey.status == SurveyStatus.CLOSED
class TestQuestionManagement:
"""题目管理测试"""
@pytest.mark.asyncio
async def test_add_question(self):
"""测试添加题目"""
mock_db = AsyncMock()
survey = MagicMock()
survey.id = 1
survey.status = SurveyStatus.DRAFT
survey.total_questions = 0
with patch.object(SurveyService, 'get_survey_by_id', return_value=survey):
# 模拟查询最大排序号
mock_result = MagicMock()
mock_result.scalar.return_value = None
with patch('app.services.survey_service.select') as mock_select:
mock_db.execute.return_value = mock_result
result = await SurveyService.add_question(
mock_db,
survey_id=1,
question_text="您对服务是否满意?",
question_type=QuestionType.SCORE
)
mock_db.add.assert_called()
@pytest.mark.asyncio
async def test_add_question_to_published_survey(self):
"""测试向已发布问卷添加题目(应失败)"""
mock_db = AsyncMock()
survey = MagicMock()
survey.id = 1
survey.status = SurveyStatus.PUBLISHED
with patch.object(SurveyService, 'get_survey_by_id', return_value=survey):
with pytest.raises(ValueError, match="只有草稿状态的问卷可以添加题目"):
await SurveyService.add_question(
mock_db,
survey_id=1,
question_text="测试题目",
question_type=QuestionType.SCORE
)
class TestResponseSubmission:
"""回答提交测试"""
@pytest.mark.asyncio
async def test_submit_response(self):
"""测试提交问卷回答"""
mock_db = AsyncMock()
# 创建模拟问卷
survey = MagicMock()
survey.id = 1
survey.status = SurveyStatus.PUBLISHED
# 创建模拟题目
question = MagicMock()
question.id = 1
question.question_type = QuestionType.SCORE
question.score_max = 5
question.options = None
# 设置mock返回值
mock_q_result = MagicMock()
mock_q_result.scalar_one_or_none.return_value = question
with patch.object(SurveyService, 'get_survey_by_id', return_value=survey):
with patch('app.services.survey_service.select') as mock_select:
mock_db.execute.return_value = mock_q_result
result = await SurveyService.submit_response(
mock_db,
survey_id=1,
department_id=1,
answers=[
{"question_id": 1, "answer_value": "4"}
]
)
mock_db.add.assert_called()
@pytest.mark.asyncio
async def test_submit_response_to_closed_survey(self):
"""测试向已结束问卷提交回答(应失败)"""
mock_db = AsyncMock()
survey = MagicMock()
survey.id = 1
survey.status = SurveyStatus.CLOSED
with patch.object(SurveyService, 'get_survey_by_id', return_value=survey):
with pytest.raises(ValueError, match="问卷未发布或已结束"):
await SurveyService.submit_response(
mock_db,
survey_id=1,
department_id=1,
answers=[]
)
class TestSatisfactionStats:
"""满意度统计测试"""
@pytest.mark.asyncio
async def test_get_department_satisfaction(self):
"""测试获取科室满意度统计"""
mock_db = AsyncMock()
# 模拟查询结果
mock_result = MagicMock()
mock_row = MagicMock()
mock_row.department_id = 1
mock_row.department_name = "内科"
mock_row.response_count = 10
mock_row.avg_satisfaction = 85.5
mock_row.total_score = 850.0
mock_row.max_score = 1000.0
mock_result.fetchall.return_value = [mock_row]
with patch('app.services.survey_service.select') as mock_select:
mock_db.execute.return_value = mock_result
result = await SurveyService.get_department_satisfaction(
mock_db,
survey_id=1
)
assert len(result) == 1
assert result[0]["department_name"] == "内科"
assert result[0]["avg_satisfaction"] == 85.5
class TestQuestionTypes:
"""不同题型测试"""
def test_score_question_options(self):
"""测试评分题选项"""
question = MagicMock()
question.question_type = QuestionType.SCORE
question.score_max = 5
question.options = None # 评分题不需要选项
# 评分题不需要选项
assert question.options is None
def test_single_choice_options(self):
"""测试单选题选项"""
import json
options = [
{"label": "非常满意", "value": "5", "score": 5},
{"label": "满意", "value": "4", "score": 4},
{"label": "一般", "value": "3", "score": 3},
{"label": "不满意", "value": "2", "score": 2}
]
question = MagicMock()
question.question_type = QuestionType.SINGLE_CHOICE
question.options = json.dumps(options, ensure_ascii=False)
parsed_options = json.loads(question.options)
assert len(parsed_options) == 4
def test_multiple_choice_options(self):
"""测试多选题选项"""
import json
options = [
{"label": "选项A", "value": "a", "score": 1},
{"label": "选项B", "value": "b", "score": 1},
{"label": "选项C", "value": "c", "score": 1}
]
question = MagicMock()
question.question_type = QuestionType.MULTIPLE_CHOICE
question.options = json.dumps(options, ensure_ascii=False)
parsed_options = json.loads(question.options)
assert len(parsed_options) == 3