能否指定Behave测试表格中单元格的数据类型?
I wrote a test case to verify that an API returns expected content when handling bad requests:
Scenario: Unkown user response body properties contain expected content Given I have a valid client auth token And I request a user with an unknown "valid" uuid And I get the response json Then the expected fields should contain expected content | field | content | | statusCode | 404 | | error | Not Found | | message | User record not found |Here's the corresponding step implementation:
@then(u'the expected fields should contain expected content') def step_impl(context): for row in context.table: received_content = str(context.request_json.get(row['field'])) expected_content = row['content'] assert_equal(received_content, expected_content)I noticed that Behave automatically converts table row content to strings. The actual
statusCodein the response is an integer, so I have to convert the response content to a string to make the assertion work. Is there a way to specify the data type for table cells in Behave? For example, mark the404in the content column as an integer so I don't have to convert the response content.
Great question! Behave doesn’t have built-in support for specifying data types directly in Gherkin tables, but there are several clean, flexible ways to handle type conversion in your step implementations. Here are a few practical approaches:
1. Field-Specific Type Mapping
If certain fields always use a specific type (like statusCode being an integer), you can define a fixed mapping of fields to their expected types. This keeps your Gherkin table unchanged while handling conversion behind the scenes:
from behave import then from nose.tools import assert_equal @then(u'the expected fields should contain expected content') def step_impl(context): # Map fields to their required data types field_type_converters = { 'statusCode': int, # Add other fields as needed (e.g., 'totalUsers': int, 'isVerified': bool) } for row in context.table: field = row['field'] received_content = context.request_json.get(field) expected_str = row['content'] # Use the converter for the field, default to string if no mapping exists converter = field_type_converters.get(field, str) try: expected_content = converter(expected_str) except ValueError: raise AssertionError(f"Failed to convert '{expected_str}' to {converter.__name__} for field '{field}'") assert_equal(received_content, expected_content)
2. Explicit Type Markers in Table Values
For more flexibility (like mixing types in the same column), add type prefixes to your table values and parse them in the step. This makes the type intent clear directly in your Gherkin:
First, update your Gherkin table with type markers:
Then the expected fields should contain expected content | field | content | | statusCode | int:404 | | error | Not Found | | message | User record not found |
Then implement a parser to handle these markers:
from behave import then from nose.tools import assert_equal def parse_typed_value(value): """Parse values with optional type prefixes (e.g., 'int:404' → 404)""" if value.startswith('int:'): return int(value.split(':', 1)[1]) elif value.startswith('float:'): return float(value.split(':', 1)[1]) elif value.lower() in ['true', 'false']: return value.lower() == 'true' # Default to string if no type marker is present return value @then(u'the expected fields should contain expected content') def step_impl(context): for row in context.table: received_content = context.request_json.get(row['field']) expected_content = parse_typed_value(row['content']) assert_equal(received_content, expected_content)
3. Add a Dedicated "Type" Column
For maximum readability, extend your table with a column that explicitly defines the data type for each row. This makes your Gherkin self-documenting for anyone reviewing the tests:
Update your Gherkin table:
Then the expected fields should contain expected content | field | type | content | | statusCode | int | 404 | | error | str | Not Found | | message | str | User record not found |
Then adjust your step implementation to use this column:
from behave import then from nose.tools import assert_equal # Map type names to conversion functions TYPE_MAP = { 'int': int, 'str': str, 'float': float, 'bool': lambda x: x.lower() == 'true' } @then(u'the expected fields should contain expected content') def step_impl(context): for row in context.table: field = row['field'] type_name = row['type'] expected_str = row['content'] converter = TYPE_MAP.get(type_name) if not converter: raise ValueError(f"Unsupported type '{type_name}' for field '{field}'") try: expected_content = converter(expected_str) except ValueError: raise AssertionError(f"Cannot convert '{expected_str}' to {type_name} for field '{field}'") received_content = context.request_json.get(field) assert_equal(received_content, expected_content)
All three methods work well—pick the one that best fits your test suite’s readability needs and flexibility requirements!
内容的提问来源于stack exchange,提问作者Jay Stringer

