Setting Up Unit Testing in an Existing Unity Project
Tags: unity3d, testing, gamedev, tutorial
Published: 2024-07-08 19:59:19

If you’re knee-deep in a Unity project and pulling your hair out trying to set up unit testing, you’re not alone. The two biggest challenges are dealing with `Assembly Definitions` and `Editor` folders. Here’s a guide to overcome these issues and make your life easier.

## The Problem

Unity, in its infinite wisdom, doesn’t allow writing tests on the default `Assembly-CSharp` and `Assembly-CSharp-Editor` assemblies. Frustrating, right?

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n7q80bdogim2ibl5j5dg.png)

To fix this, you need to add `Assembly Definitions` for both your game code and editor code. Here’s how:

1. **Game Code:**
   - Add an `Assembly Definition` at the root of your project.
   - Update the dependencies in the Inspector until there are no compiler errors. Off to the races!

2. **Editor Code:**
   - Here’s where it gets tricky. In a big project, you’ll have lots of external dependencies and plugins, some with `Assembly Definitions`, some without.
   - Without `Assembly Definitions`, Unity sees these `Editor` folders as game code and tries to build them into your final game, which inevitably fails.

## The Solution

1. **Create a Top-Level Editor Folder:**
   - At the top level of your project, create an `Editor` folder.
   - Add an `Assembly Definition` inside this folder. Name it similarly to your game code `Assembly Definition` but with a `.Editor` postfix (or whatever you prefer).

2. **Link Editor Folders:**
   - For each `Editor` folder in your project, create an `Assembly Definition Reference` and link it to the top-level `Editor` folder `Assembly Definition`.
   - This process can be incredibly tedious in a large project, so here’s a Python script to save you from hours of mind-numbing work:

```python
import os
import json
import yaml

def get_guid_from_meta(meta_file_path):
    with open(meta_file_path, 'r') as meta_file:
        meta_content = yaml.safe_load(meta_file)
    return meta_content.get('guid')

def create_reference_json(editor_path, source_file_name, guid):
    json_content = {
        "reference": "GUID:" + guid
    }
    asmref_file_name = os.path.splitext(source_file_name)[0] + '.asmref'
    json_file_path = os.path.join(editor_path, asmref_file_name)
    with open(json_file_path, 'w') as json_file:
        json.dump(json_content, json_file, indent=4)
    print(f"Created {json_file_path} with GUID reference.")

def copy_file_to_editor_folders(unity_project_path, source_file):
    if not os.path.isdir(unity_project_path):
        print(f"The project path {unity_project_path} does not exist.")
        return

    meta_file_path = source_file + '.meta'
    if not os.path.isfile(meta_file_path):
        print(f"The meta file {meta_file_path} does not exist.")
        return
    guid = get_guid_from_meta(meta_file_path)

    source_file_name = os.path.basename(source_file)

    for root, dirs, files in os.walk(unity_project_path):
        if 'Editor' in dirs:
            editor_path = os.path.join(root, 'Editor')
            asmdef_exists = any(file.endswith('.asmdef') for file in os.listdir(editor_path))
            
            if not asmdef_exists:
                create_reference_json(editor_path, source_file_name, guid)
            else:
                print(f"A .asmdef file already exists in the folder {editor_path}.")

# Example usage
unity_project_path = r"PATH\TO\ASSETS\FOLDER"
source_file = r"PATH\TO\Editor.asmdef"

copy_file_to_editor_folders(unity_project_path, source_file)
```

This script requires `pyyaml` to run:

```bash
pip install pyyaml
```

- Set `unity_project_path` to your Unity project’s assets folder.
- Set `source_file` to the path of your `Editor` `Assembly Definition`.

The script will:
- Crawl your project for all `Editor` folders.
- Check if an `Assembly Definition` is present.
- Add an `Assembly Definition Reference` if none exists.

After setting this up, follow the [Unity Testing Framework tutorial](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/getting-started.html) to start writing tests.

#### Additional Tips

Consider splitting your code with `Assembly Definitions` to create logical boundaries and improve maintainability. It might be a pain now, but your future self will thank you!

Happy testing!