mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-09 16:18:30 +00:00
Refactor: Improve test organization and add edge case tests
Co-Authored-By: Joe Moura <joao@crewai.com>
This commit is contained in:
@@ -12,9 +12,228 @@ from crewai.tasks.output_format import OutputFormat
|
|||||||
from crewai.tasks.task_output import TaskOutput
|
from crewai.tasks.task_output import TaskOutput
|
||||||
|
|
||||||
|
|
||||||
|
class TestMultipleConditionalTasks:
|
||||||
|
"""Test class for multiple conditional tasks scenarios."""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def setup_agents(self):
|
||||||
|
"""Set up agents for the tests."""
|
||||||
|
agent1 = Agent(
|
||||||
|
role="Research Analyst",
|
||||||
|
goal="Find information",
|
||||||
|
backstory="You're a researcher",
|
||||||
|
verbose=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
agent2 = Agent(
|
||||||
|
role="Data Analyst",
|
||||||
|
goal="Process information",
|
||||||
|
backstory="You process data",
|
||||||
|
verbose=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
agent3 = Agent(
|
||||||
|
role="Report Writer",
|
||||||
|
goal="Write reports",
|
||||||
|
backstory="You write reports",
|
||||||
|
verbose=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return agent1, agent2, agent3
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def setup_tasks(self, setup_agents):
|
||||||
|
"""Set up tasks for the tests."""
|
||||||
|
agent1, agent2, agent3 = setup_agents
|
||||||
|
|
||||||
|
# Create tasks
|
||||||
|
task1 = Task(
|
||||||
|
description="Task 1",
|
||||||
|
expected_output="Output 1",
|
||||||
|
agent=agent1,
|
||||||
|
)
|
||||||
|
|
||||||
|
# First conditional task should check task1's output
|
||||||
|
condition1_mock = MagicMock()
|
||||||
|
task2 = ConditionalTask(
|
||||||
|
description="Conditional Task 2",
|
||||||
|
expected_output="Output 2",
|
||||||
|
agent=agent2,
|
||||||
|
condition=condition1_mock,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Second conditional task should check task1's output, not task2's
|
||||||
|
condition2_mock = MagicMock()
|
||||||
|
task3 = ConditionalTask(
|
||||||
|
description="Conditional Task 3",
|
||||||
|
expected_output="Output 3",
|
||||||
|
agent=agent3,
|
||||||
|
condition=condition2_mock,
|
||||||
|
)
|
||||||
|
|
||||||
|
return task1, task2, task3, condition1_mock, condition2_mock
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def setup_crew(self, setup_agents, setup_tasks):
|
||||||
|
"""Set up crew for the tests."""
|
||||||
|
agent1, agent2, agent3 = setup_agents
|
||||||
|
task1, task2, task3, _, _ = setup_tasks
|
||||||
|
|
||||||
|
crew = Crew(
|
||||||
|
agents=[agent1, agent2, agent3],
|
||||||
|
tasks=[task1, task2, task3],
|
||||||
|
verbose=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return crew
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def setup_task_outputs(self, setup_agents):
|
||||||
|
"""Set up task outputs for the tests."""
|
||||||
|
agent1, agent2, _ = setup_agents
|
||||||
|
|
||||||
|
task1_output = TaskOutput(
|
||||||
|
description="Task 1",
|
||||||
|
raw="Task 1 output",
|
||||||
|
agent=agent1.role,
|
||||||
|
output_format=OutputFormat.RAW,
|
||||||
|
)
|
||||||
|
|
||||||
|
task2_output = TaskOutput(
|
||||||
|
description="Conditional Task 2",
|
||||||
|
raw="Task 2 output",
|
||||||
|
agent=agent2.role,
|
||||||
|
output_format=OutputFormat.RAW,
|
||||||
|
)
|
||||||
|
|
||||||
|
return task1_output, task2_output
|
||||||
|
|
||||||
|
def test_first_conditional_task_execution(self, setup_crew, setup_tasks, setup_task_outputs):
|
||||||
|
"""Test that the first conditional task is evaluated correctly."""
|
||||||
|
crew = setup_crew
|
||||||
|
_, task2, _, condition1_mock, _ = setup_tasks
|
||||||
|
task1_output, _ = setup_task_outputs
|
||||||
|
|
||||||
|
condition1_mock.return_value = True # Task should execute
|
||||||
|
result = crew._handle_conditional_task(
|
||||||
|
task=task2,
|
||||||
|
task_outputs=[task1_output],
|
||||||
|
futures=[],
|
||||||
|
task_index=1,
|
||||||
|
was_replayed=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify the condition was called with task1's output
|
||||||
|
condition1_mock.assert_called_once()
|
||||||
|
args = condition1_mock.call_args[0][0]
|
||||||
|
assert args.raw == "Task 1 output"
|
||||||
|
assert result is None # Task should execute, so no skipped output
|
||||||
|
|
||||||
|
def test_second_conditional_task_execution(self, setup_crew, setup_tasks, setup_task_outputs):
|
||||||
|
"""Test that the second conditional task is evaluated correctly."""
|
||||||
|
crew = setup_crew
|
||||||
|
_, _, task3, _, condition2_mock = setup_tasks
|
||||||
|
task1_output, task2_output = setup_task_outputs
|
||||||
|
|
||||||
|
condition2_mock.return_value = True # Task should execute
|
||||||
|
result = crew._handle_conditional_task(
|
||||||
|
task=task3,
|
||||||
|
task_outputs=[task1_output, task2_output],
|
||||||
|
futures=[],
|
||||||
|
task_index=2,
|
||||||
|
was_replayed=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify the condition was called with task1's output, not task2's
|
||||||
|
condition2_mock.assert_called_once()
|
||||||
|
args = condition2_mock.call_args[0][0]
|
||||||
|
assert args.raw == "Task 1 output" # Should be task1's output
|
||||||
|
assert args.raw != "Task 2 output" # Should not be task2's output
|
||||||
|
assert result is None # Task should execute, so no skipped output
|
||||||
|
|
||||||
|
def test_conditional_task_skipping(self, setup_crew, setup_tasks, setup_task_outputs):
|
||||||
|
"""Test that conditional tasks are skipped when the condition returns False."""
|
||||||
|
crew = setup_crew
|
||||||
|
_, task2, _, condition1_mock, _ = setup_tasks
|
||||||
|
task1_output, _ = setup_task_outputs
|
||||||
|
|
||||||
|
condition1_mock.return_value = False # Task should be skipped
|
||||||
|
result = crew._handle_conditional_task(
|
||||||
|
task=task2,
|
||||||
|
task_outputs=[task1_output],
|
||||||
|
futures=[],
|
||||||
|
task_index=1,
|
||||||
|
was_replayed=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify the condition was called with task1's output
|
||||||
|
condition1_mock.assert_called_once()
|
||||||
|
args = condition1_mock.call_args[0][0]
|
||||||
|
assert args.raw == "Task 1 output"
|
||||||
|
assert result is not None # Task should be skipped, so there should be a skipped output
|
||||||
|
assert result.description == task2.description
|
||||||
|
|
||||||
|
def test_conditional_task_with_explicit_context(self, setup_crew, setup_agents, setup_task_outputs):
|
||||||
|
"""Test conditional task with explicit context tasks."""
|
||||||
|
crew = setup_crew
|
||||||
|
agent1, agent2, _ = setup_agents
|
||||||
|
task1_output, _ = setup_task_outputs
|
||||||
|
|
||||||
|
with patch.object(crew, '_find_task_index', return_value=0):
|
||||||
|
context_task = Task(
|
||||||
|
description="Task 1",
|
||||||
|
expected_output="Output 1",
|
||||||
|
agent=agent1,
|
||||||
|
)
|
||||||
|
|
||||||
|
condition_mock = MagicMock(return_value=True)
|
||||||
|
task_with_context = ConditionalTask(
|
||||||
|
description="Task with Context",
|
||||||
|
expected_output="Output with Context",
|
||||||
|
agent=agent2,
|
||||||
|
condition=condition_mock,
|
||||||
|
context=[context_task],
|
||||||
|
)
|
||||||
|
|
||||||
|
crew.tasks.append(task_with_context)
|
||||||
|
|
||||||
|
result = crew._handle_conditional_task(
|
||||||
|
task=task_with_context,
|
||||||
|
task_outputs=[task1_output],
|
||||||
|
futures=[],
|
||||||
|
task_index=3, # This would be the 4th task
|
||||||
|
was_replayed=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify the condition was called with task1's output
|
||||||
|
condition_mock.assert_called_once()
|
||||||
|
args = condition_mock.call_args[0][0]
|
||||||
|
assert args.raw == "Task 1 output"
|
||||||
|
assert result is None # Task should execute, so no skipped output
|
||||||
|
|
||||||
|
def test_conditional_task_with_empty_task_outputs(self, setup_crew, setup_tasks):
|
||||||
|
"""Test conditional task with empty task outputs."""
|
||||||
|
crew = setup_crew
|
||||||
|
_, task2, _, condition1_mock, _ = setup_tasks
|
||||||
|
|
||||||
|
result = crew._handle_conditional_task(
|
||||||
|
task=task2,
|
||||||
|
task_outputs=[],
|
||||||
|
futures=[],
|
||||||
|
task_index=1,
|
||||||
|
was_replayed=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
condition1_mock.assert_not_called()
|
||||||
|
assert result is None # Task should execute, so no skipped output
|
||||||
|
|
||||||
|
|
||||||
def test_multiple_conditional_tasks():
|
def test_multiple_conditional_tasks():
|
||||||
"""Test that multiple conditional tasks are evaluated correctly."""
|
"""Test that multiple conditional tasks are evaluated correctly.
|
||||||
# Create agents for the tasks
|
|
||||||
|
This is a legacy test that's kept for backward compatibility.
|
||||||
|
The actual tests are now in the TestMultipleConditionalTasks class.
|
||||||
|
"""
|
||||||
agent1 = Agent(
|
agent1 = Agent(
|
||||||
role="Research Analyst",
|
role="Research Analyst",
|
||||||
goal="Find information",
|
goal="Find information",
|
||||||
@@ -61,56 +280,56 @@ def test_multiple_conditional_tasks():
|
|||||||
condition=condition2_mock,
|
condition=condition2_mock,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create crew with the tasks
|
|
||||||
crew = Crew(
|
crew = Crew(
|
||||||
agents=[agent1, agent2, agent3],
|
agents=[agent1, agent2, agent3],
|
||||||
tasks=[task1, task2, task3],
|
tasks=[task1, task2, task3],
|
||||||
verbose=True,
|
verbose=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test the first conditional task
|
with patch.object(crew, '_find_task_index', return_value=0):
|
||||||
task1_output = TaskOutput(
|
task1_output = TaskOutput(
|
||||||
description="Task 1",
|
description="Task 1",
|
||||||
raw="Task 1 output",
|
raw="Task 1 output",
|
||||||
agent=agent1.role,
|
agent=agent1.role,
|
||||||
output_format=OutputFormat.RAW,
|
output_format=OutputFormat.RAW,
|
||||||
)
|
)
|
||||||
|
|
||||||
condition1_mock.return_value = True # Task should execute
|
condition1_mock.return_value = True # Task should execute
|
||||||
result1 = crew._handle_conditional_task(
|
result1 = crew._handle_conditional_task(
|
||||||
task=task2,
|
task=task2,
|
||||||
task_outputs=[task1_output],
|
task_outputs=[task1_output],
|
||||||
futures=[],
|
futures=[],
|
||||||
task_index=1,
|
task_index=1,
|
||||||
was_replayed=False,
|
was_replayed=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify the condition was called with task1's output
|
# Verify the condition was called with task1's output
|
||||||
condition1_mock.assert_called_once()
|
condition1_mock.assert_called_once()
|
||||||
args1 = condition1_mock.call_args[0][0]
|
args1 = condition1_mock.call_args[0][0]
|
||||||
assert args1.raw == "Task 1 output"
|
assert args1.raw == "Task 1 output"
|
||||||
assert result1 is None # Task should execute, so no skipped output
|
assert result1 is None # Task should execute, so no skipped output
|
||||||
|
|
||||||
# Test the second conditional task
|
condition1_mock.reset_mock()
|
||||||
task2_output = TaskOutput(
|
|
||||||
description="Conditional Task 2",
|
task2_output = TaskOutput(
|
||||||
raw="Task 2 output",
|
description="Conditional Task 2",
|
||||||
agent=agent2.role,
|
raw="Task 2 output",
|
||||||
output_format=OutputFormat.RAW,
|
agent=agent2.role,
|
||||||
)
|
output_format=OutputFormat.RAW,
|
||||||
|
)
|
||||||
condition2_mock.return_value = True # Task should execute
|
|
||||||
result2 = crew._handle_conditional_task(
|
condition2_mock.return_value = True # Task should execute
|
||||||
task=task3,
|
result2 = crew._handle_conditional_task(
|
||||||
task_outputs=[task1_output, task2_output],
|
task=task3,
|
||||||
futures=[],
|
task_outputs=[task1_output, task2_output],
|
||||||
task_index=2,
|
futures=[],
|
||||||
was_replayed=False,
|
task_index=2,
|
||||||
)
|
was_replayed=False,
|
||||||
|
)
|
||||||
# Verify the condition was called with task1's output, not task2's
|
|
||||||
condition2_mock.assert_called_once()
|
# Verify the condition was called with task1's output, not task2's
|
||||||
args2 = condition2_mock.call_args[0][0]
|
condition2_mock.assert_called_once()
|
||||||
assert args2.raw == "Task 1 output" # Should be task1's output
|
args2 = condition2_mock.call_args[0][0]
|
||||||
assert args2.raw != "Task 2 output" # Should not be task2's output
|
assert args2.raw == "Task 1 output" # Should be task1's output
|
||||||
assert result2 is None # Task should execute, so no skipped output
|
assert args2.raw != "Task 2 output" # Should not be task2's output
|
||||||
|
assert result2 is None # Task should execute, so no skipped output
|
||||||
|
|||||||
Reference in New Issue
Block a user