Refactor: Improve test organization and add edge case tests

Co-Authored-By: Joe Moura <joao@crewai.com>
This commit is contained in:
Devin AI
2025-04-07 11:11:45 +00:00
parent cbac6a5534
commit 214357c482

View File

@@ -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