开发编程
#python
python-testing
pytest、TDD手法、フィクスチャ、モック、パラメータ化、カバレッジ要件を使用したPythonテスト戦略。
DeepseekModel
官方收录技能
质量 优秀 · 90
v1.0.0
获取
https://deepseekmodel.com/api/download.php?id=affaan-m-ecc-docs-ja-jp-skills-python-testing-skill-md&format=skill
下载 .skill
标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用
.skill 文件中 system_prompt 字段的实际内容。
name python-testing description pytest、TDD手法、フィクスチャ、モック、パラメータ化、カバレッジ要件を使用したPythonテスト戦略。 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."""
Agent 识别该技能的关键词,点击任意一个即可复制。
该技能未提供触发词。
下载的 .skill 包内含以下字段。
| 字段 | 说明 |
|---|---|
| format | 格式标识(skill/v1) |
| skill_id | 技能唯一 ID |
| name | 技能名称 |
| version | 版本号 |
| description | 技能描述 |
| category | 所属分类(数组) |
| trigger_words | 触发词列表 |
| tags | 标签列表 |
| source | 来源标识 |
| source_url | 来源链接(本页地址) |
| exported_at | 导出时间(每次下载生成) |
| system_prompt | 系统提示词正文 |
| model_config | 模型参数:provider / model / temperature / max_tokens / top_p |
| examples | 示例 |
| install_guide | 各平台导入说明(Coze / Dify / Claude / 自定义框架) |