add backend source code
This commit is contained in:
279
backend/tests/test_survey_service.py
Normal file
279
backend/tests/test_survey_service.py
Normal file
@@ -0,0 +1,279 @@
|
||||
"""
|
||||
满意度调查服务测试用例
|
||||
|
||||
测试覆盖:
|
||||
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
|
||||
Reference in New Issue
Block a user