Enhanced Mock Testing System
This section covers advanced mocking features for developers extending the Brobot framework or building complex testing scenarios.
For standard automation testing, see the main Testing documentation.
Brobot's enhanced mock testing system provides sophisticated scenario-based testing capabilities with advanced failure patterns, verification, and structured test data management.
Overviewโ
The enhanced mock system extends the basic mocking capabilities with:
- Centralized mock mode management via
MockModeManager - Scenario-based configurations for complex test conditions
- Advanced failure patterns with temporal and cascading behaviors
- Behavioral verification beyond simple operation counting
- Structured test data builders with variations and versioning
- Performance optimization for large-scale test execution
Centralized Mock Mode Managementโ
Brobot now provides the MockModeManager class as a single source of truth for mock mode configuration:
For Spring-based tests, Brobot provides centralized mock mode management via MockModeManager and BrobotTestBase.
import io.github.jspinak.brobot.config.MockModeManager;
import io.github.jspinak.brobot.config.BrobotProperties;
import org.springframework.beans.factory.annotation.Autowired;
public class MyMockTest {
// BrobotProperties must be injected as a dependency
@Autowired
private BrobotProperties brobotProperties;
public void configureMockMode() {
// Enable mock mode across all components
MockModeManager.setMockMode(true);
// Check if mock mode is active
if (brobotProperties.getCore().isMock()) {
// Execute mock-specific logic
}
// Debug mock mode state
MockModeManager.logMockModeState();
}
}
This ensures consistency across:
- System properties
- ExecutionEnvironment
- FrameworkSettings
- All other mock-aware components
Mock Scenario Configurationโ
Basic Scenario Setupโ
The examples in this section reference mockScenarioManager, which is not included in the main Brobot library. It exists only as an example implementation in /examples/03-core-library/testing/enhanced-mocking/.
To use these features, you'll need to:
- Implement your own MockScenarioManager based on the example, or
- Use MockScenarioConfig directly with
scenario.activate()method - See
/examples/directory for reference implementation
All test classes should extend BrobotTestBase for automatic mock mode configuration:
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.mock.scenario.MockScenarioConfig;
import io.github.jspinak.brobot.action.ActionResult;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class MyScenarioTest extends BrobotTestBase {
@Test
public void testLoginUnderNetworkIssues() {
MockScenarioConfig scenario = MockScenarioConfig.builder()
.scenarioName("login_network_issues")
.description("Simulate intermittent network connectivity during login")
.stateAppearanceProbability("LOGIN_STATE", 0.8) // 80% appear rate
.stateAppearanceProbability("DASHBOARD", 0.9) // 90% appear rate
.build();
// Note: MockScenarioManager.activateScenario() is from examples, not main library
// Use scenario.activate() or implement your own manager
// mockScenarioManager.activateScenario(scenario);
// Your test logic here
ActionResult result = actions.find(loginButton);
assertTrue(result.isSuccess());
}
}
Advanced Failure Patternsโ
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.mock.scenario.FailurePattern;
import io.github.jspinak.brobot.tools.testing.mock.scenario.MockScenarioConfig;
import io.github.jspinak.brobot.action.ActionType;
import io.github.jspinak.brobot.action.ActionResult;
import io.github.jspinak.brobot.action.options.PatternFindOptions;
import org.junit.jupiter.api.Test;
import java.time.Duration;
public class CascadingFailuresTest extends BrobotTestBase {
@Test
public void testRetryBehaviorWithCascadingFailures() {
// Configure cascading failures that worsen over time
FailurePattern cascadingFailure = FailurePattern.builder()
.baseProbability(0.3) // Start with 30% failure rate
.cascading(true) // Enable cascading
.cascadeMultiplier(1.5) // Each failure increases probability by 50%
.maxConsecutiveFailures(3) // Force success after 3 failures
.recoveryDelay(Duration.ofSeconds(2)) // 2-second recovery period
.failureMessage("Network timeout")
.build();
MockScenarioConfig scenario = MockScenarioConfig.builder()
.scenarioName("cascading_network_failures")
.actionFailurePattern(ActionType.FIND, cascadingFailure)
.maxDuration(Duration.ofMinutes(5)) // Scenario timeout
.build();
mockScenarioManager.activateScenario(scenario);
// Test retry logic
for (int attempt = 1; attempt <= 5; attempt++) {
// Find with pause before next retry
PatternFindOptions findWithPause = new PatternFindOptions.Builder()
.setPauseAfterEnd(0.5) // 500ms pause if not found
.build();
ActionResult result = actions.perform(findWithPause, targetElement);
if (result.isSuccess()) break;
}
}
}
Temporal Conditionsโ
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.mock.scenario.TemporalConditions;
import io.github.jspinak.brobot.tools.testing.mock.scenario.MockScenarioConfig;
import io.github.jspinak.brobot.action.ActionResult;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import java.time.LocalTime;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TemporalConditionsTest extends BrobotTestBase {
@Test
public void testPerformanceUnderLoad() {
TemporalConditions slowNetwork = TemporalConditions.builder()
.baseDelay(Duration.ofMillis(500)) // Base 500ms delay
.maximumDelay(Duration.ofSeconds(3)) // Cap at 3 seconds
.delayProgression(Duration.ofMillis(100)) // Increase by 100ms each time
.randomVariation(0.2) // ยฑ20% random variation
.activeTimeRange(LocalTime.of(9, 0), LocalTime.of(17, 0)) // Business hours
.build();
MockScenarioConfig scenario = MockScenarioConfig.builder()
.scenarioName("performance_degradation")
.temporalCondition("slow_network", slowNetwork)
.build();
mockScenarioManager.activateScenario(scenario);
long startTime = System.currentTimeMillis();
ActionResult result = actions.find(targetElement);
long duration = System.currentTimeMillis() - startTime;
assertTrue("Action should take longer under load", duration >= 500);
assertTrue(result.isSuccess());
}
}
Behavioral Verificationโ
State Transition Verificationโ
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.mock.verification.StateTransitionVerification;
import io.github.jspinak.brobot.tools.testing.mock.verification.VerificationResult;
import io.github.jspinak.brobot.tools.testing.mock.verification.MockBehaviorVerifier;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class StateTransitionTest extends BrobotTestBase {
@Autowired
private MockBehaviorVerifier mockBehaviorVerifier;
@Test
public void testLoginFlowTransitionSequence() {
// Set up verification for expected state transitions
StateTransitionVerification verification = mockBehaviorVerifier
.expectTransitionSequence("login_flow")
.fromState("INITIAL")
.toState("LOGIN_PAGE")
.maxDuration(Duration.ofSeconds(2)) // Transition should be fast
.fromState("LOGIN_PAGE")
.toState("AUTHENTICATING")
.minDuration(Duration.ofMillis(100)) // Should take some time to authenticate
.maxDuration(Duration.ofSeconds(5))
.fromState("AUTHENTICATING")
.toState("DASHBOARD")
.withinTime(Duration.ofSeconds(10)) // Overall sequence timeout
.verify();
// Execute the login flow
performLoginSequence();
// Verify the transitions occurred as expected
assertEquals(VerificationResult.PASSED, verification.getResult());
assertTrue("No transition errors", verification.getErrors().isEmpty());
}
}
Action Pattern Verificationโ
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.mock.verification.ActionPatternVerification;
import io.github.jspinak.brobot.tools.testing.mock.verification.VerificationResult;
import io.github.jspinak.brobot.tools.testing.mock.verification.MockBehaviorVerifier;
import io.github.jspinak.brobot.action.ActionType;
import io.github.jspinak.brobot.action.ActionResult;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ActionPatternTest extends BrobotTestBase {
@Autowired
private MockBehaviorVerifier mockBehaviorVerifier;
@Test
public void testRetryPatternCompliance() {
// Verify that find operations retry with proper backoff
ActionPatternVerification retryVerification = mockBehaviorVerifier
.expectActionPattern("find_retry_pattern")
.action(ActionType.FIND)
.maxAttempts(3)
.withBackoff(Duration.ofMillis(500))
.expectedSuccessRate(0.8) // 80% should eventually succeed
.within(Duration.ofSeconds(10))
.verify();
// Execute actions that may need retries
ActionResult result1 = actions.find(intermittentElement);
ActionResult result2 = actions.find(intermittentElement);
ActionResult result3 = actions.find(intermittentElement);
// Verify retry behavior
assertEquals(VerificationResult.PASSED, retryVerification.getResult());
assertTrue("Retry timing should be correct", retryVerification.getErrors().isEmpty());
}
}
Structured Test Data Builderโ
Creating Test Scenariosโ
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.data.TestDataBuilder;
import io.github.jspinak.brobot.tools.testing.data.TestScenario;
import io.github.jspinak.brobot.datatypes.state.stateObject.stateImage.StateImage;
import io.github.jspinak.brobot.datatypes.primitiveStructs.region.Region;
import io.github.jspinak.brobot.action.ActionResult;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestDataBuilderExample extends BrobotTestBase {
@Autowired
private TestDataBuilder testDataBuilder;
@Test
public void testWithStructuredData() {
TestScenario loginScenario = testDataBuilder
.scenario("comprehensive_login_test")
.withDescription("Complete login flow with variations")
.withVersion("1.1.0")
.withBaselineData()
.withStateImage("login_button", "login_btn.png")
.withStateImage("username_field", "username_input.png")
.withStateString("welcome_text", "Welcome back!")
.withRegion("login_form", new Region(300, 200, 400, 300))
.withTag("authentication")
.withTag("critical_path")
.build();
// Use the structured scenario in your test
StateImage loginButton = loginScenario.getStateImages().get("login_button");
Region formArea = loginScenario.getRegions().get("login_form");
ActionResult result = actions.find(loginButton).searchRegions(formArea);
assertTrue(result.isSuccess());
}
}
Test Variationsโ
The TestDataBuilder variations API is partially complete. StateImage transformation methods shown below are still in development. See TestDataBuilder.java TODOs for current status.
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.data.TestDataBuilder;
import io.github.jspinak.brobot.tools.testing.data.TestScenario;
import io.github.jspinak.brobot.datatypes.state.stateObject.stateImage.StateImage;
import io.github.jspinak.brobot.action.ActionResult;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestVariationsExample extends BrobotTestBase {
@Autowired
private TestDataBuilder testDataBuilder;
@Test
public void testMobileLayoutVariation() {
TestScenario baseScenario = testDataBuilder.loginScenario().build();
// Create mobile variation with adjusted similarity thresholds
TestScenario mobileScenario = baseScenario.withVariation("small_screen");
// Mobile scenario automatically has:
// - Reduced similarity thresholds for scaled elements
// - Adjusted regions for smaller screen
// - Modified timing expectations
StateImage mobileLoginButton = mobileScenario.getStateImages().get("login_button");
// Note: StateImage similarity checking API may vary - check actual implementation
// assertTrue("Mobile button has lower similarity threshold",
// mobileLoginButton.getSimilarity() < 0.8);
ActionResult result = actions.find(mobileLoginButton);
assertTrue(result.isSuccess());
}
}
Custom Variationsโ
The transformation API shown below references StateImage methods (.toBuilder(), .getSimilarity()) that are still being implemented. This is an aspirational API design. Check the actual TestDataBuilder and StateImage classes for current capabilities.
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.data.TestDataBuilder;
import io.github.jspinak.brobot.tools.testing.data.TestScenario;
import io.github.jspinak.brobot.datatypes.state.stateObject.stateImage.StateImage;
import io.github.jspinak.brobot.action.ActionResult;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class CustomVariationsExample extends BrobotTestBase {
@Autowired
private TestDataBuilder testDataBuilder;
@Test
public void testHighContrastVariation() {
TestScenario scenario = testDataBuilder
.scenario("high_contrast_test")
.withStateImage("button", "normal_button.png")
.withVariation("high_contrast")
.withDescription("High contrast accessibility mode")
.withTransformation("reduce_similarity", (name, obj) -> {
// Note: This transformation API is under development
if (obj instanceof StateImage) {
// StateImage.toBuilder() not yet implemented
// return ((StateImage) obj).toBuilder()
// .similarity(Math.max(0.6, ((StateImage) obj).getSimilarity() - 0.15))
// .build();
}
return obj;
})
.endVariation()
.build();
TestScenario highContrastScenario = scenario.withVariation("high_contrast");
// Test with high contrast variation
ActionResult result = actions.find(
highContrastScenario.getStateImages().get("button"));
assertTrue(result.isSuccess());
}
}
Base Test Configurationโ
Using BrobotTestBaseโ
All Brobot tests should extend BrobotTestBase to ensure proper mock mode configuration and consistent test behavior across different environments:
import io.github.jspinak.brobot.test.BrobotTestBase;
import org.junit.jupiter.api.Test;
public class MyBrobotTest extends BrobotTestBase {
@Test
public void testMyFeature() {
// Your test code here
// Mock mode is automatically enabled
}
}
What BrobotTestBase Providesโ
BrobotTestBase automatically configures the following for all tests:
- Mock Mode Activation - Sets
// Mock mode is now configured via application.properties: // brobot.core.mock=trueto prevent SikuliX headless exceptions - Fast Mock Timings - Configures minimal delays for mock operations (0.01-0.04 seconds)
- Mouse Settings - Removes mouse pause delays for faster test execution
- Screenshot Paths - Sets up paths for mock screenshot operations
- Per-Test Reset - Ensures mock mode remains enabled between tests
Key Benefitsโ
- CI/CD Compatibility - Tests run without requiring a display or GUI
- Consistent Behavior - All tests use the same mock configuration
- Fast Execution - Minimal mock delays speed up test suites
- Headless Support - Prevents AWTException and HeadlessException errors
Customizing Test Setupโ
You can override setupTest() to add custom configuration:
import io.github.jspinak.brobot.test.BrobotTestBase;
import org.junit.jupiter.api.BeforeEach;
public class CustomTest extends BrobotTestBase {
@Override
@BeforeEach
public void setupTest() {
super.setupTest(); // Important: call parent setup first
// Add your custom setup
// Configure custom mock timings (example - adjust as needed)
System.setProperty("brobot.mock.time.find.first", "0.05");
// Other custom configuration...
}
}
When to Use BrobotTestBaseโ
- Always for unit tests that use Brobot APIs
- Always for integration tests in headless environments
- Optional for end-to-end tests that need real screen interaction (don't extend BrobotTestBase)
Enhanced Mock Infrastructureโ
Grid Operations in Mock Modeโ
Brobot now provides full grid operation support in mock mode through the MockGridConfig class:
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.mock.grid.MockGridConfig;
import io.github.jspinak.brobot.datatypes.primitiveStructs.region.Region;
import io.github.jspinak.brobot.datatypes.primitiveStructs.location.Location;
import io.github.jspinak.brobot.tools.region.RegionUtils;
import org.junit.jupiter.api.Test;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class GridOperationsTest extends BrobotTestBase {
@Test
public void testGridOperations() {
// Configure grid dimensions for testing
MockGridConfig.setDefaultGrid(3, 3); // 3x3 grid
Region region = new Region(0, 0, 300, 300);
Location location = new Location(150, 150); // Center
// Grid operations work seamlessly in mock mode
Optional<Integer> gridNumber = RegionUtils.getGridNumber(region, location);
assertTrue(gridNumber.isPresent());
assertEquals(4, gridNumber.get()); // Center cell in 3x3 grid
// Get specific grid region
Region gridRegion = region.getGridRegion(4);
assertEquals(100, gridRegion.w());
assertEquals(100, gridRegion.h());
}
}
MockGridConfig Featuresโ
- Configurable Dimensions: Set custom grid sizes with
setDefaultGrid(rows, cols) - Thread-Safe: Safe for use in parallel test execution
- Fallback Calculations: When SikuliX is unavailable, uses native Brobot calculations
- Consistent Behavior: Same API in mock and real modes
MockFind Intelligent Fallback Behaviorโ
The MockFind class provides intelligent fallback behavior to ensure robust testing even when match history is not configured:
Automatic Match Generationโ
When a pattern has no configured match history, MockFind automatically generates default successful matches:
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.mock.action.MockFind;
import io.github.jspinak.brobot.datatypes.state.stateObject.otherStateObjects.Pattern;
import io.github.jspinak.brobot.datatypes.primitiveStructs.match.Match;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MockFindFallbackTest extends BrobotTestBase {
@Autowired
private ApplicationContext context;
@Test
public void testWithoutMatchHistory() {
// Create pattern without any match history
Pattern pattern = new Pattern.Builder()
.setName("TestPattern")
.build();
// MockFind will still return a match in mock mode
MockFind mockFind = context.getBean(MockFind.class);
List<Match> matches = mockFind.getMatches(pattern);
assertFalse(matches.isEmpty(), "MockFind provides default match");
assertEquals("TestPattern", matches.get(0).getName());
}
}
Fallback Behavior Rulesโ
MockFind follows these rules for consistent testing behavior:
- Check for existing history: First checks if the pattern has any match history configured
- Generate default match if empty: If no history exists, generates a default successful match with:
- Region: 100, 100, 50, 50 (reasonable default location and size)
- Similarity score: 0.95 (high confidence match)
- Name: Uses pattern name if available, otherwise "MockMatch"
- Respect configured history: If history exists but has no matches for current state, returns empty list
- State-aware matching: When history exists, filters by active states
Example: Testing Without Extensive Setupโ
@Test
public void testQuickMockSetup() {
// No need to configure match history for simple tests
StateImage quickTestImage = new StateImage.Builder()
.addPattern(new Pattern.Builder()
.setName("QuickTestPattern")
.build())
.build();
ObjectCollection collection = new ObjectCollection.Builder()
.withImages(quickTestImage)
.build();
// MockFind will provide default matches
ActionResult result = action.perform(new PatternFindOptions.Builder().build(), collection);
assertTrue(result.isSuccess(), "Mock mode works without match history setup");
}
When to Use Match History vs Fallbackโ
-
Use configured match history when:
- Testing specific match locations or patterns
- Simulating test scenarios with varying success rates
- Testing state-specific behaviors
-
Rely on fallback behavior when:
- Running quick smoke tests
- Testing flow logic rather than match specifics
- Prototyping new test scenarios
- Testing in CI/CD without complex setup
Configuring Match History When Neededโ
For tests requiring specific match behavior, configure match history explicitly:
@Test
public void testWithSpecificMatchHistory() {
Pattern pattern = new Pattern.Builder()
.setName("SpecificPattern")
.build();
// Add specific match history
ActionRecord successfulFind = new ActionRecord.Builder()
.setActionConfig(new PatternFindOptions.Builder()
.setStrategy(PatternFindOptions.Strategy.FIRST)
.build())
.setActionSuccess(true)
.setState("TestState")
.setMatchList(Arrays.asList(
new Match.Builder()
.setRegion(new Region(200, 150, 100, 50)) // Specific location
.setSimScore(0.98) // High confidence
.build()))
.build();
pattern.getMatchHistory().addSnapshot(successfulFind);
// Test will use configured history instead of fallback
MockFind mockFind = context.getBean(MockFind.class);
List<Match> matches = mockFind.getMatches(pattern);
assertEquals(200, matches.get(0).getRegion().x()); // Uses configured location
}
This intelligent fallback behavior ensures that:
- Tests can run successfully without extensive mock setup
- Quick prototyping and testing is possible
- Tests remain maintainable with minimal configuration
- Configured history is always respected when present
Mock Scene and Color Analysisโ
The MockSceneBuilder provides comprehensive builders for creating test data for color analysis and scene processing:
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.mock.builders.MockSceneBuilder;
import io.github.jspinak.brobot.imageUtils.analysis.Scene;
import io.github.jspinak.brobot.imageUtils.analysis.SceneAnalysis;
import io.github.jspinak.brobot.imageUtils.analysis.PixelProfiles;
import io.github.jspinak.brobot.colorUtils.clustering.ColorCluster;
import io.github.jspinak.brobot.colorUtils.clustering.ColorClassifier;
import org.opencv.core.Mat;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MockSceneBuilderTest extends BrobotTestBase {
@Test
public void testColorAnalysis() {
// Create a mock scene with initialized image data
Scene scene = MockSceneBuilder.createMockScene();
assertNotNull(scene.getPattern());
assertNotNull(scene.getPattern().getImage());
// Create scene analysis with multiple profiles
SceneAnalysis analysis = MockSceneBuilder.createMockSceneAnalysis(3);
assertEquals(3, analysis.size());
// Each profile has properly initialized color clusters
PixelProfiles profile = analysis.getPixelAnalysisCollection(0);
assertNotNull(profile.getStateImage().getColorCluster());
// Color operations work without real images
ColorClassifier classifier = new ColorClassifier();
Mat indices = classifier.getImageIndices(analysis, ColorCluster.ColorSchemaName.BGR);
assertNotNull(indices);
}
}
MockSceneBuilder Methodsโ
| Method | Description |
|---|---|
createMockScene() | Creates Scene with valid Pattern and Image |
createMockPattern() | Creates Pattern with BGR Mat image |
createMockSceneAnalysis(int) | Creates SceneAnalysis with specified number of profiles |
createMockColorCluster() | Creates ColorCluster with BGR and HSV schemas |
createMockColorSchema(ColorSchemaName) | Creates schema with proper statistics |
sceneAnalysis() | Returns builder for complex SceneAnalysis configurations |
Builder Pattern for Complex Scenariosโ
import io.github.jspinak.brobot.test.BrobotTestBase;
import io.github.jspinak.brobot.tools.testing.mock.builders.MockSceneBuilder;
import io.github.jspinak.brobot.imageUtils.analysis.SceneAnalysis;
import io.github.jspinak.brobot.imageUtils.analysis.Scene;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ComplexSceneTest extends BrobotTestBase {
@Test
public void testComplexSceneAnalysis() {
// Create custom scene first
Scene customScene = MockSceneBuilder.createMockScene();
// Use builder for complex configurations
SceneAnalysis analysis = MockSceneBuilder.sceneAnalysis()
.withScene(customScene)
.withPixelProfile(0)
.withPixelProfile(1)
.withPixelProfile(2)
.build();
// Analysis is fully initialized and ready for testing
assertEquals(3, analysis.getPixelAnalysisCollections().size());
}
}
Performance Characteristicsโ
Mock mode operations are significantly faster than real operations:
The timing values below represent configured mock delays (0.01-0.04s), not measured benchmarks. Real mode timings are estimates based on typical SikuliX/OpenCV operations. Actual performance varies by system configuration and operation complexity.
| Operation | Mock Mode (Configured) | Real Mode (Estimated) | Expected Speedup |
|---|---|---|---|
| Grid operations | ~0.01s | 0.5s | ~50x |
| Color analysis | ~0.02s | 1-2s | ~50-100x |
| Pattern matching | ~0.01s | 0.5-2s | ~50-200x |
| State transitions | ~0.01s | 0.2-1s | ~20-100x |
Jackson Serialization Supportโ
When creating test objects that need serialization, ensure proper Jackson annotations:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder(toBuilder = true, builderClassName = "Builder")
@JsonDeserialize(builder = MyTestData.Builder.class)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MyTestData {
private String field;
@JsonPOJOBuilder(withPrefix = "")
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Builder {
// Lombok generates implementation
}
}
Common Testing Pitfalls and Solutionsโ
| Pitfall | Solution |
|---|---|
| Tests fail in headless environments | Always extend BrobotTestBase |
| SikuliX field mocking errors | Use real SikuliX objects or Brobot mocks |
| Grid operations return empty | Configure MockGridConfig dimensions |
| ColorClassifier NPEs | Use MockSceneBuilder for test data |
Forget to call super.setupTest() | Always call parent setup when overriding |
| Static mocks without cleanup | Use try-with-resources for static mocks |
Test Suite Organizationโ
import io.github.jspinak.brobot.tools.testing.data.TestDataBuilder;
import io.github.jspinak.brobot.tools.testing.mock.scenario.MockScenarioConfig;
import io.github.jspinak.brobot.tools.testing.mock.scenario.FailurePattern;
import io.github.jspinak.brobot.tools.testing.mock.scenario.TemporalConditions;
import io.github.jspinak.brobot.action.ActionType;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
@TestConfiguration
public class MockTestConfig {
// Note: MockScenarioManager is from examples, not main library
// Implement your own or use MockScenarioConfig directly
@Bean
public TestDataBuilder testDataBuilder() {
return new TestDataBuilder();
}
// Pre-configured scenarios for reuse
@Bean
public Map<String, MockScenarioConfig> commonScenarios() {
Map<String, MockScenarioConfig> scenarios = new HashMap<>();
scenarios.put("network_issues", MockScenarioConfig.builder()
.scenarioName("network_issues")
.actionFailurePattern(ActionType.FIND,
FailurePattern.builder()
.baseProbability(0.2)
.maxConsecutiveFailures(2)
.build())
.build());
scenarios.put("slow_system", MockScenarioConfig.builder()
.scenarioName("slow_system")
.temporalCondition("delay",
TemporalConditions.builder()
.baseDelay(Duration.ofMillis(200))
.build())
.build());
return scenarios;
}
}
Best Practicesโ
Scenario Designโ
- Keep scenarios focused - Each scenario should test one specific condition
- Use meaningful names - Scenario names should clearly indicate what they test
- Set appropriate timeouts - Prevent runaway tests with reasonable duration limits
- Document expected behaviors - Include descriptions of what each scenario validates
Failure Pattern Designโ
- Model realistic failures - Base patterns on actual system behavior
- Use progressive failures - Start with low probability and increase over time
- Include recovery periods - Allow systems to recover after failure sequences
- Set failure limits - Prevent infinite failure loops with max consecutive failures
Verification Strategyโ
- Verify behavior, not just results - Check timing, sequences, and patterns
- Use multiple verification types - Combine state transitions with action patterns
- Include negative tests - Verify that unexpected behaviors are caught
- Clean up after tests - Reset verifiers and scenarios between tests
Performance Considerationsโ
- Use sampling for high-frequency actions - Reduce verification overhead
- Batch related verifications - Group similar checks together
- Clean up resources - Properly dispose of mock contexts and verifiers
- Monitor test execution time - Enhanced mocking should not significantly slow tests
Configuration Referenceโ
MockScenarioConfig Propertiesโ
| Property | Type | Description |
|---|---|---|
scenarioName | String | Unique identifier for the scenario |
description | String | Human-readable description |
stateAppearanceProbabilities | Map<String, Double> | Per-state appearance rates (0.0-1.0) |
actionFailurePatterns | Map<Action, FailurePattern> | Failure patterns by action type |
temporalConditions | Map<String, TemporalConditions> | Time-based conditions |
maxDuration | Duration | Maximum scenario runtime |
cascadingFailures | boolean | Enable failure cascading |
FailurePattern Propertiesโ
| Property | Type | Description |
|---|---|---|
baseProbability | double | Base failure rate (0.0-1.0) |
probabilityDecay | double | Reduction per failure occurrence |
maxConsecutiveFailures | int | Max failures before forced success |
cascading | boolean | Whether failures increase probability |
recoveryDelay | Duration | Recovery time after failures |
exceptionType | Class<Exception> | Type of exception to throw |
Verification Configurationโ
| Property | Type | Description |
|---|---|---|
maxTotalTime | Duration | Overall sequence timeout |
minDuration | Duration | Minimum step duration |
maxDuration | Duration | Maximum step duration |
optional | boolean | Whether step is optional |
verificationWindow | Duration | Time window for pattern verification |
Related Documentationโ
Core Testing Guidesโ
- Testing Introduction - Overview of Brobot testing approaches
- Unit Testing Guide - Writing unit tests with Brobot
- Integration Testing Guide - Integration test patterns and practices
- Mock Mode Guide - Fundamentals of mock mode configuration
- Test Utilities - BrobotTestBase, BrobotTestUtils, and testing helpers
- Mat Testing Utilities - OpenCV Mat testing utilities for color analysis
- CI/CD Testing - CI/CD testing best practices
ActionConfig and Actionsโ
- Action Config Overview - ActionConfig class hierarchy
- ActionResult Components - ActionResult reference
- ActionConfig Reference - Complete API reference including PatternFindOptions
Testing Strategyโ
- Testing Strategy - Overall testing philosophy and approach
This enhanced mock testing system provides comprehensive tools for creating realistic, maintainable, and thorough test scenarios that closely mirror production conditions while enabling rapid iteration and debugging.