I recently switched from Pycharm to Cursor and am now experiencing different behaviors when running behave tests using gherkin/cucumber syntax.
It appears that the way pycharm and vscode handle python functionality is different and vscode lacks a lot of features.
My tests are creating context variables in the before all/before features/ etc setup that have to be used throughout the tests and updated.
For example, i send the driver to initialize all my page objects since these tests are running in the UI across many different applications.
from e2e_threats.api_objects.alert_management_services.services import AlertManagementServiceManager
from e2e_threats.api_objects.aws import AWSServiceManager
from e2e_threats.api_objects.intelligence_center.services import IntelligenceCenterServiceManager
from e2e_threats.api_objects.notification_center.api_base_page import APIBasePage
from e2e_threats.api_objects.notification_center.live_feed import LiveFeedAPI
from e2e_threats.api_objects.notification_center.subscription import SubscriptionAPI
from e2e_threats.api_objects.notification_center.travel_briefs import TravelBriefAPI
from e2e_threats.page_objects.base_page import BasePage
from e2e_threats.page_objects.intelligence_center.alert import ICAlertPage
from e2e_threats.page_objects.intelligence_center.chat import ICChatPage
from e2e_threats.page_objects.intelligence_center.configuration import ICConfigurationPage
from e2e_threats.page_objects.intelligence_center.content_management import ICContentManagementPage
from e2e_threats.page_objects.intelligence_center.dashboard import ICDashboardPage
from e2e_threats.page_objects.intelligence_center.gator.column import GATORColumnPage
from e2e_threats.page_objects.intelligence_center.tiny_mce import TinyMCEPage
from e2e_threats.page_objects.notification_center.chat import NCChatPage
from e2e_threats.page_objects.notification_center.content.situation_reports import NCSituationReportPage
from e2e_threats.page_objects.notification_center.content.travel_briefs import NCTravelBriefPage
from e2e_threats.page_objects.notification_center.dashboard import NCDashboardPage
from e2e_threats.page_objects.notification_center.live_feed import NCLiveFeedPage
from e2e_threats.page_objects.notification_center.subscription import NCSubscriptionPage
class PageObjectsBank(object):
def __init__(self, driver=None, environment=None):
self.ams_services_page = AlertManagementServiceManager(environment)
self.api_base_page = APIBasePage(driver)
self.aws_api_page = AWSServiceManager(environment)
self.base_page = BasePage(driver)
self.ic_services_page = IntelligenceCenterServiceManager(environment)
self.tiny_mce_page = TinyMCEPage(driver)
self.ic_alert_page = ICAlertPage(driver)
self.ic_chat_page = ICChatPage(driver)
self.ic_configuration_page = ICConfigurationPage(driver)
self.ic_content_management_page = ICContentManagementPage(driver)
self.ic_dashboard_page = ICDashboardPage(driver)
self.gator_column_page = GATORColumnPage(driver)
self.nc_chat_page = NCChatPage(driver)
self.nc_dashboard_page = NCDashboardPage(driver)
self.nc_live_feed_api_page = LiveFeedAPI(driver)
self.nc_live_feed_page = NCLiveFeedPage(driver)
self.nc_situation_report_page = NCSituationReportPage(driver)
self.nc_subscription_api_page = SubscriptionAPI(driver)
self.nc_subscription_page = NCSubscriptionPage(driver)
self.nc_travel_brief_api_page = TravelBriefAPI(driver, environment)
self.nc_travel_brief_page = NCTravelBriefPage(driver)
def before_feature(c, feature):
if 'skip' in feature.tags:
feature.skip('Marked with @skip')
return
for scenario in feature.walk_scenarios():
patch_scenario_with_autoretry(scenario, max_attempts=2)
c.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=c.chrome_options)
c.page_objects_bank = PageObjectsBank(driver=c.driver, environment=c.env)
# Threat Warnings Setup
c.page_objects_bank.ams_services_page.configure_host(c.env)
c.redis_client = c.page_objects_bank.ams_services_page.setup_redis()
c.intel_redis_client = c.page_objects_bank.ic_services_page.setup_intel_redis()
c.ams_client = c.page_objects_bank.ams_services_page.initialize_ams_client()
# clean up subscriptions in NC
c.page_objects_bank.nc_subscription_api_page.cleanup_subscriptions(c)
# clean up live feeds in NC
c.page_objects_bank.nc_live_feed_api_page.cleanup_live_feed(c)
I used to be able to ctrl+click on the function that exists in the page object (c.page_objects_bank.ic_alert_page.clean_up), but now i cannot. *The tests run successfully in debugger/terminal since the context is initiated at runtime, but it’s when trying to navigate in the IDE where i experience issues making it really hard to write new/maintain tests.
@when('user selects "(.*)" on "(.*)" alerts')
def step_impl(c, clean_type, status):
clean_type = validate_clean_type(clean_type)
status = validate_alert_status(status)
c.page_objects_bank.ic_alert_page.clean_up(clean_type, status)
Cursor described this issue as such:
The difference in how VSCode/Cursor and PyCharm handle dynamic vs. static objects comes down to their code analysis engines:
- Different Code Analysis Approaches:
- PyCharm uses a more sophisticated Python-specific code analysis engine that was built from the ground up for Python
- VSCode/Cursor use the Pylance/Pyright language server which, while powerful, has a different approach to tracking references
- Type Inference Capabilities:
- PyCharm has more advanced type inference for dynamic Python code
- PyCharm better understands Python’s dynamic nature, tracking object attributes across method calls and variable assignments
- Context Tracking:
- PyCharm maintains a more complete context model of your entire project
- It’s better at tracking the “behave” context object (c) and understanding what attributes are attached to it
- Index Depth and Completeness:
- PyCharm indexes your entire project more thoroughly by default
- VSCode/Cursor limit indexing depth to save resources, which is why we needed those python.analysis.packageIndexDepths settings
- Debugging and Navigation Integration:
- PyCharm’s debugger is more tightly integrated with its code understanding
- When debugging in PyCharm, it has better knowledge of object types at runtime and can use this to improve navigation
- Framework-Specific Intelligence:
- PyCharm has specific built-in understanding of popular frameworks (including testing frameworks)
- It understands patterns like the Page Object Model or Behave’s context passing better
The key reason PyCharm handles these dynamic objects better is that it was designed specifically as a Python IDE with years of refinement for Python’s dynamic nature. VSCode is a more general-purpose editor with Python support added through extensions, so it sometimes struggles with Python’s more dynamic patterns like those used in your test framework.
For VSCode/Cursor to better handle your code, you’d need either:
- More explicit type hints
- A more static design pattern
- Custom configuration like the indexing depth settings we discussed
Are there any plans to support this? Or something i can do differently?
I am using the Cucumber (Gherkin) Full Support, Python (anysphere), ms-python, Python Debugger plugins.
My settings.json file under .vscode:
{
"editor.formatOnSave": true,
"python.languageServer": "Default",
"python.analysis.typeCheckingMode": "basic",
"python.analysis.extraPaths": [
"${workspaceFolder}"
],
"python.defaultInterpreterPath": "${workspaceFolder}/venv/bin/python",
"python.terminal.activateEnvironment": true,
"editor.quickSuggestions": {
"comments": false,
"strings": true,
"other": true
},
"cucumberautocomplete.steps": [
"ams/steps/**/*.py",
"e2e_threats/steps/**/*.py"
],
"cucumberautocomplete.syncfeatures": "ams/features/**/*.feature,e2e_threats/features/**/*.feature",
"cucumberautocomplete.strictGherkinCompletion": false,
"cucumberautocomplete.strictGherkinValidation": false,
"cucumberautocomplete.smartSnippets": true,
"cucumberautocomplete.customParameters": [
{
"parameter": "(IC|NC|GATOR)",
"value": "<app>"
}
]
}