mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-21 22:18:04 +02:00
feat: security overhaul (proxy ssrf fix, streaming sanitization, docker optimization)
This commit is contained in:
1 parent
6d87ada2b8
commit
8df9a37baa
810 files changed
+131090
-151
No files matched your search
@@ -0,0 +1,383 @@
|
||||
# Unity Test Framework Guide
|
||||
|
||||
## Overview
|
||||
|
||||
Unity provides a built-in Test Framework based on NUnit for writing and running automated tests. It supports Edit Mode tests (run without playing) and Play Mode tests (run during gameplay simulation).
|
||||
|
||||
## Test Framework Setup
|
||||
|
||||
### Package Installation
|
||||
|
||||
```json
|
||||
// manifest.json - usually pre-installed
|
||||
{
|
||||
"dependencies": {
|
||||
"com.unity.test-framework": "1.6.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
Assets/
|
||||
├── Scripts/
|
||||
│ └── Runtime/
|
||||
│ ├── Player/
|
||||
│ │ └── PlayerController.cs
|
||||
│ └── Combat/
|
||||
│ └── DamageCalculator.cs
|
||||
└── Tests/
|
||||
├── EditMode/
|
||||
│ └── DamageCalculatorTests.cs
|
||||
└── PlayMode/
|
||||
└── PlayerMovementTests.cs
|
||||
```
|
||||
|
||||
### Assembly Definitions
|
||||
|
||||
Create `.asmdef` files for test assemblies:
|
||||
|
||||
```json
|
||||
// Tests/EditMode/EditModeTests.asmdef
|
||||
{
|
||||
"name": "EditModeTests",
|
||||
"references": ["GameAssembly"],
|
||||
"includePlatforms": ["Editor"],
|
||||
"defineConstraints": ["UNITY_INCLUDE_TESTS"]
|
||||
}
|
||||
|
||||
// Tests/PlayMode/PlayModeTests.asmdef
|
||||
{
|
||||
"name": "PlayModeTests",
|
||||
"references": ["GameAssembly"],
|
||||
"includePlatforms": [],
|
||||
"defineConstraints": ["UNITY_INCLUDE_TESTS"]
|
||||
}
|
||||
```
|
||||
|
||||
## Edit Mode Tests
|
||||
|
||||
Edit Mode tests run in the Editor without entering Play Mode. Best for testing pure logic.
|
||||
|
||||
### Basic Test Structure
|
||||
|
||||
```csharp
|
||||
using NUnit.Framework;
|
||||
|
||||
[TestFixture]
|
||||
public class DamageCalculatorTests
|
||||
{
|
||||
private DamageCalculator _calculator;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_calculator = new DamageCalculator();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Calculate_BaseDamage_ReturnsCorrectValue()
|
||||
{
|
||||
float result = _calculator.Calculate(100f, 1f);
|
||||
Assert.AreEqual(100f, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Calculate_CriticalHit_DoublesDamage()
|
||||
{
|
||||
float result = _calculator.Calculate(100f, multiplier: 2f);
|
||||
Assert.AreEqual(200f, result);
|
||||
}
|
||||
|
||||
[TestCase(100f, 0.5f, 50f)]
|
||||
[TestCase(100f, 1.5f, 150f)]
|
||||
[TestCase(50f, 2f, 100f)]
|
||||
public void Calculate_Parameterized_ReturnsExpected(
|
||||
float base_, float mult, float expected)
|
||||
{
|
||||
Assert.AreEqual(expected, _calculator.Calculate(base_, mult));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Testing ScriptableObjects
|
||||
|
||||
```csharp
|
||||
[Test]
|
||||
public void WeaponStats_DPS_CalculatesCorrectly()
|
||||
{
|
||||
var weapon = ScriptableObject.CreateInstance<WeaponStats>();
|
||||
weapon.baseDamage = 10f;
|
||||
weapon.attacksPerSecond = 2f;
|
||||
|
||||
Assert.AreEqual(20f, weapon.DPS);
|
||||
|
||||
// Cleanup
|
||||
Object.DestroyImmediate(weapon);
|
||||
}
|
||||
```
|
||||
|
||||
## Play Mode Tests
|
||||
|
||||
Play Mode tests run during gameplay simulation. Required for testing MonoBehaviours, physics, and runtime behavior.
|
||||
|
||||
### Basic Play Mode Test
|
||||
|
||||
```csharp
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
public class PlayerMovementTests
|
||||
{
|
||||
private GameObject _player;
|
||||
private PlayerController _controller;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_player = new GameObject("Player");
|
||||
_controller = _player.AddComponent<PlayerController>();
|
||||
_player.AddComponent<Rigidbody>();
|
||||
_player.AddComponent<CapsuleCollider>();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Object.Destroy(_player);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Move_WhenInputApplied_ChangesPosition()
|
||||
{
|
||||
Vector3 startPos = _player.transform.position;
|
||||
|
||||
_controller.SetInput(Vector2.right);
|
||||
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
|
||||
Assert.Greater(_player.transform.position.x, startPos.x);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Jump_WhenGrounded_BecomesAirborne()
|
||||
{
|
||||
// Setup ground
|
||||
var ground = GameObject.CreatePrimitive(PrimitiveType.Plane);
|
||||
_player.transform.position = Vector3.up;
|
||||
|
||||
yield return new WaitForFixedUpdate();
|
||||
|
||||
_controller.Jump();
|
||||
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
|
||||
Assert.IsFalse(_controller.IsGrounded);
|
||||
|
||||
Object.Destroy(ground);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Testing Coroutines
|
||||
|
||||
```csharp
|
||||
[UnityTest]
|
||||
public IEnumerator Attack_Cooldown_PreventsSpam()
|
||||
{
|
||||
_controller.Attack();
|
||||
Assert.IsTrue(_controller.IsAttacking);
|
||||
|
||||
_controller.Attack(); // Should be blocked
|
||||
Assert.AreEqual(1, _controller.AttackCount);
|
||||
|
||||
yield return new WaitForSeconds(_controller.AttackCooldown + 0.1f);
|
||||
|
||||
_controller.Attack();
|
||||
Assert.AreEqual(2, _controller.AttackCount);
|
||||
}
|
||||
```
|
||||
|
||||
### Scene Testing
|
||||
|
||||
```csharp
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator MainMenu_StartButton_LoadsGameScene()
|
||||
{
|
||||
SceneManager.LoadScene("MainMenu");
|
||||
yield return null; // Wait for scene load
|
||||
|
||||
var startButton = GameObject.Find("StartButton")
|
||||
.GetComponent<Button>();
|
||||
startButton.onClick.Invoke();
|
||||
|
||||
yield return new WaitForSeconds(1f);
|
||||
|
||||
Assert.AreEqual("GameScene", SceneManager.GetActiveScene().name);
|
||||
}
|
||||
```
|
||||
|
||||
## Integration Test Patterns
|
||||
|
||||
### Prefab Testing
|
||||
|
||||
```csharp
|
||||
[UnityTest]
|
||||
public IEnumerator EnemyPrefab_Spawns_WithCorrectComponents()
|
||||
{
|
||||
var prefab = Resources.Load<GameObject>("Prefabs/Enemy");
|
||||
var instance = Object.Instantiate(prefab);
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsNotNull(instance.GetComponent<EnemyAI>());
|
||||
Assert.IsNotNull(instance.GetComponent<Health>());
|
||||
Assert.IsNotNull(instance.GetComponent<NavMeshAgent>());
|
||||
|
||||
Object.Destroy(instance);
|
||||
}
|
||||
```
|
||||
|
||||
### Input System Testing
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator InputAction_Fire_TriggersWeapon()
|
||||
{
|
||||
var keyboard = InputSystem.AddDevice<Keyboard>();
|
||||
|
||||
yield return null;
|
||||
|
||||
Press(keyboard.spaceKey);
|
||||
yield return null;
|
||||
|
||||
Assert.IsTrue(_controller.IsFiring);
|
||||
|
||||
Release(keyboard.spaceKey);
|
||||
yield return null;
|
||||
|
||||
Assert.IsFalse(_controller.IsFiring);
|
||||
}
|
||||
```
|
||||
|
||||
## Test Utilities
|
||||
|
||||
### Custom Assertions
|
||||
|
||||
```csharp
|
||||
public static class GameAssert
|
||||
{
|
||||
public static void AreApproximatelyEqual(
|
||||
Vector3 expected, Vector3 actual, float tolerance = 0.001f)
|
||||
{
|
||||
Assert.AreEqual(expected.x, actual.x, tolerance);
|
||||
Assert.AreEqual(expected.y, actual.y, tolerance);
|
||||
Assert.AreEqual(expected.z, actual.z, tolerance);
|
||||
}
|
||||
|
||||
public static void IsWithinRange(float value, float min, float max)
|
||||
{
|
||||
Assert.GreaterOrEqual(value, min);
|
||||
Assert.LessOrEqual(value, max);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Test Fixtures
|
||||
|
||||
```csharp
|
||||
public class TestScene : IDisposable
|
||||
{
|
||||
public GameObject Player { get; private set; }
|
||||
public GameObject Ground { get; private set; }
|
||||
|
||||
public TestScene()
|
||||
{
|
||||
Ground = GameObject.CreatePrimitive(PrimitiveType.Plane);
|
||||
Player = Object.Instantiate(Resources.Load<GameObject>("Player"));
|
||||
Player.transform.position = Vector3.up;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Object.Destroy(Player);
|
||||
Object.Destroy(Ground);
|
||||
}
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Player_FallsToGround()
|
||||
{
|
||||
using var scene = new TestScene();
|
||||
|
||||
yield return new WaitForSeconds(1f);
|
||||
|
||||
Assert.Less(scene.Player.transform.position.y, 0.5f);
|
||||
}
|
||||
```
|
||||
|
||||
## CI Integration
|
||||
|
||||
### Command Line Execution
|
||||
|
||||
```bash
|
||||
# Run Edit Mode tests
|
||||
Unity -runTests -batchmode -projectPath . \
|
||||
-testPlatform EditMode \
|
||||
-testResults results.xml
|
||||
|
||||
# Run Play Mode tests
|
||||
Unity -runTests -batchmode -projectPath . \
|
||||
-testPlatform PlayMode \
|
||||
-testResults results.xml
|
||||
```
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
```yaml
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: game-ci/unity-test-runner@v4
|
||||
with:
|
||||
projectPath: .
|
||||
testMode: all
|
||||
artifactsPath: TestResults
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### DO
|
||||
|
||||
- Test pure logic in Edit Mode (faster execution)
|
||||
- Use Play Mode only when needed (physics, coroutines, MonoBehaviour)
|
||||
- Create test fixtures for common setups
|
||||
- Clean up created GameObjects in TearDown
|
||||
- Use `[Category]` attributes for test organization
|
||||
- Run tests before every commit
|
||||
|
||||
### DON'T
|
||||
|
||||
- Don't test Unity's built-in functionality
|
||||
- Don't rely on specific frame timing (use WaitForSeconds)
|
||||
- Don't leave test objects in scenes
|
||||
- Don't test private methods directly (test through public API)
|
||||
- Don't create tests that depend on execution order
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
| ---------------------- | ------------------ | ------------------------------------------ |
|
||||
| Tests not appearing | Missing asmdef | Create test assembly definition |
|
||||
| NullReferenceException | Missing Setup | Ensure [SetUp] initializes all fields |
|
||||
| Tests hang | Infinite coroutine | Add timeout or max iterations |
|
||||
| Flaky physics tests | Timing dependent | Use WaitForFixedUpdate, increase tolerance |
|
||||
Reference in new issue
Block a user