開発
#python
python-testing
使用pytest的Python测试策略,包括TDD方法、夹具、模拟、参数化和覆盖率要求。
DeepseekModel
キュレーション済みスキル
品質 優秀 · 90
v1.0.0
取得
https://deepseekmodel.com/api/download.php?id=affaan-m-ecc-docs-zh-cn-skills-python-testing-skill-md&format=skill
ダウンロード .skill
標準形式。system_prompt と model_config を収録し、任意の Agent で利用可能
.skill ファイルの system_prompt フィールドの実際の内容。
name python-testing description 使用pytest的Python测试策略,包括TDD方法、夹具、模拟、参数化和覆盖率要求。 origin ECC Python 测试模式 使用 pytest、TDD 方法论和最佳实践的 Python 应用程序全面测试策略。 何时激活 编写新的 Python 代码(遵循 TDD:红、绿、重构) 为 Python 项目设计测试套件 审查 Python 测试覆盖率 设置测试基础设施 核心测试理念 测试驱动开发 (TDD) 始终遵循 TDD 循环: 红 :为期望的行为编写一个失败的测试 绿 :编写最少的代码使测试通过 重构 :在保持测试通过的同时改进代码 # Step 1: Write failing test (RED) def test_add_numbers (): result = add( 2 , 3 ) assert result == 5 # Step 2: Write minimal implementation (GREEN) def add ( a, b ): return a + b # Step 3: Refactor if needed (REFACTOR) 覆盖率要求 目标 :80%+ 代码覆盖率 关键路径 :需要 100% 覆盖率 使用 pytest --cov 来测量覆盖率 pytest --cov=mypackage --cov-report=term-missing --cov-report=html pytest 基础 基本测试结构 import pytest def test_addition (): """Test basic addition.""" assert 2 + 2 == 4 def test_string_uppercase (): """Test string uppercasing.""" text = "hello" assert text.upper() == "HELLO" def test_list_append (): """Test list append.""" items = [ 1 , 2 , 3 ] items.append( 4 ) assert 4 in items assert len (items) == 4 断言 # Equality assert result == expected # Inequality assert result != unexpected # Truthiness assert result # Truthy assert not result # Falsy assert result is True # Exactly True assert result is False # Exactly False assert result is None # Exactly None # Membership assert item in collection assert item not in collection # Comparisons assert result > 0 assert 0 <= result <= 100 # Type checking assert isinstance (result, str ) # Exception testing (preferred approach) with pytest.raises(ValueError): raise ValueError( "error message" ) # Check exception message with pytest.raises(ValueError, match = "invalid input" ): raise ValueError( "invalid input provided" ) # Check exception attributes with pytest.raises(ValueError) as exc_info: raise ValueError( "error message" ) assert str (exc_info.value) == "error message" 夹具 基本夹具使用 import pytest @pytest.fixture def sample_data (): """Fixture providing sample data.""" return { "name" : "Alice" , "age" : 30 } def test_sample_data ( sample_data ): """Test using the fixture.""" assert sample_data[ "name" ] == "Alice" assert sample_data[ "age" ] == 30 带设置/拆卸的夹具 @pytest.fixture def database (): """Fixture with setup and teardown.""" # Setup db = Database( ":memory:" ) db.create_tables() db.insert_test_data() yield db # Provide to test # Teardown db.close() def test_database_query ( database ): """Test database operations.""" result = database.query( "SELECT * FROM users" ) assert len (result) > 0 夹具作用域 # Function scope (default) - runs for each test @pytest.fixture def temp_file (): with open ( "temp.txt" , "w" ) as f: yield f os.remove( "temp.txt" ) # Module scope - runs once per module @pytest.fixture( scope= "module" ) def module_db (): db = Database( ":memory:" ) db.create_tables() yield db db.close() # Session scope - runs once per test session @pytest.fixture( scope= "session" ) def shared_resource (): resource = ExpensiveResource() yield resource resource.cleanup() 带参数的夹具 @pytest.fixture( params=[ 1 , 2 , 3 ] ) def number ( request ): """Parameterized fixture.""" return request.param def test_numbers ( number ): """Test runs 3 times, once for each parameter.""" assert number > 0 使用多个夹具 @pytest.fixture def user (): return User( id = 1 , name= "Alice" ) @pytest.fixture def admin (): return User( id = 2 , name= "Admin" , role= "admin" ) def test_user_admin_interaction ( user, admin ): """Test using multiple fixtures.""" assert admin.can_manage(user) 自动使用夹具 @pytest.fixture( autouse= True ) def reset_config (): """Automatically runs before every test.""" Config.reset() yield Config.cleanup() def test_without_fixture_call (): # reset_config runs automatically assert Config.get_setting( "debug" ) is False 使用 Conftest.py 共享夹具 # tests/conftest.py import pytest @pytest.fixture def client (): """Shared fixture for all tests.""" app = create_app(testing= True ) with app.test_client() as client: yield client @pytest.fixture def auth_headers ( client ): """Generate auth headers for API testing.""" response = client.post( "/api/login" , json={ "username" : "test" , "password" : "test" }) token = response.json[ "token" ] return { "Authorization" : f"Bearer {token} " } 参数化 基本参数化 @pytest.mark.parametrize( "input,expected" , [ ( "hello" , "HELLO" ), ( "world" , "WORLD" ), ( "PyThOn" , "PYTHON" ), ] ) def test_uppercase ( input , expected ): """Test runs 3 times with different inputs.""" assert input .upper() == expected 多参数 @pytest.mark.parametrize( "a,b,expected" , [ ( 2 , 3 , 5 ), ( 0 , 0 , 0 ), ( - 1 , 1 , 0 ), ( 100 , 200 , 300 ), ] ) def test_add ( a, b, expected ): """Test addition with multiple inputs.""" assert add(a, b) == expected 带 ID 的参数化 @pytest.mark.parametrize( "input,expected" , [ ( "valid@email.com" , True ), ( "invalid" , False ), ( "@no-domain.com" , False ), ], ids=[ "valid-email" , "missing-at" , "missing-domain" ] ) def test_email_validation ( input , expected ): """Test email validation with readable test IDs.""" assert is_valid_email( input ) is expected 参数化夹具 @pytest.fixture( params=[ "sqlite" , "postgresql" , "mysql" ] ) def db ( request ): """Test against multiple database backends.""" if request.param == "sqlite" : return Database( ":memory:" ) elif request.param == "postgresql" : return Database( "postgresql://localhost/test" ) elif request.param == "mysql" : return Database( "mysql://localhost/test" ) def test_database_operations ( db ): """Test runs 3 times, once for each database.""" result = db.query( "SELECT 1" ) assert result is not None 标记器和测试选择 自定义标记器 # Mark slow tests @pytest.mark.slow def test_slow_operation (): time.sleep( 5 ) # Mark integration tests @pytest.mark.integration def test_api_integration (): response = requests.get( "https://api.example.com" ) assert response.status_code == 200 # Mark unit tests @pytest.mark.unit def test_unit_logic (): assert calculate( 2 , 3 ) == 5 运行特定测试 # Run only fast tests pytest -m "not slow" # Run only integration tests pytest -m integration # Run integration or slow tests pytest -m "integration or slow" # Run tests marked as unit but not slow pytest -m "unit and not slow" 在 pytest.ini 中配置标记器 [pytest] markers = slow: marks tests as slow integration: marks tests as integration tests unit: marks tests as unit tests django: marks tests as requiring Django 模拟和补丁 模拟函数 from unittest.mock import patch, Mock @patch( "mypackage.external_api_call" ) def test_with_mock ( api_call_mock ): """Test with mocked external API.""" api_call_mock.return_value = { "status" : "success" } result = my_function() api_call_mock.assert_called_once() assert result[ "status" ] == "success" 模拟返回值 @patch( "mypackage.Database.connect" ) def test_database_connection ( connect_mock ): """Test with mocked database connection.""" connect_mock.return_value = MockConnection() db = Database() db.connect() connect_mock.assert_called_once_with( "localhost" ) 模拟异常 @patch( "mypackage.api_call" ) def test_api_error_handling ( api_call_mock ): """Test error handling with mocked exception.""" api_call_mock.side_effect = ConnectionError( "Network error" ) with pytest.raises(ConnectionError): api_call() api_call_mock.assert_called_once() 模拟上下文管理器 @patch( "builtins.open" , new_callable=mock_open ) def test_file_reading ( mock_file ): """Test file reading with mocked open.""" mock_file.return_value.read.return_value = "file content" result = read_file( "test.txt" ) mock_file.assert_called_once_with( "test.txt" , "r" ) assert result == "file content" 使用 Autospec @patch( "mypackage.DBConnection" , autospec= True ) def test_autospec ( db_mock ): """Test with autospec to catch API misuse.""" db = db_mock.return_value db.query( "SELECT * FROM users" ) # This would fail if DBConnection doesn't have query method db_mock.assert_called_once() 模拟类实例 class TestUserService : @patch( "mypackage.UserRepository" ) def test_create_user ( self, repo_mock ): """Test user creation with mocked repository.""" repo_mock.return_value.save.return_value = User( id = 1 , name= "Alice" ) service = UserService(repo_mock.return_value) user = service.create_user(name= "Alice" ) assert user.name == "Alice" repo_mock.return_value.save.assert_called_once() 模拟属性 @pytest.fixture def mock_config (): """Create a mock with a property.""" config = Mock() type (config).debug = PropertyMock(return_value= True ) type (config).api_key = PropertyMock(return_value= "test-key" ) return config def test_with_mock_config ( mock_config ): """Test with mocked config properties.""" assert mock_config.debug is True assert mock_config.api_key == "test-key" 测试异步代码 使用 pytest-asyncio 进行异步测试 import pytest @pytest.mark.asyncio async def test_async_function (): """Test async function.""" result = await async_add( 2 , 3 )
このスキルを起動するキーワード。クリックでコピーできます。
このスキルにはトリガーワードがありません。
ダウンロードした .skill に含まれるフィールド。
| フィールド | 説明 |
|---|---|
| format | フォーマット識別子(skill/v1) |
| skill_id | スキル固有 ID |
| name | スキル名 |
| version | バージョン |
| description | 説明 |
| category | カテゴリ(配列) |
| trigger_words | トリガーワード |
| tags | タグ |
| source | ソース |
| source_url | ソース URL(本ページ) |
| exported_at | エクスポート日時(ダウンロード毎) |
| system_prompt | システムプロンプト本文 |
| model_config | モデル設定:provider / model / temperature / max_tokens / top_p |
| examples | サンプル |
| install_guide | 各プラットフォームの導入説明(Coze / Dify / Claude / カスタム) |