TestTheSpire 0.1.7

dotnet add package TestTheSpire --version 0.1.7
                    
NuGet\Install-Package TestTheSpire -Version 0.1.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TestTheSpire" Version="0.1.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TestTheSpire" Version="0.1.7" />
                    
Directory.Packages.props
<PackageReference Include="TestTheSpire" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TestTheSpire --version 0.1.7
                    
#r "nuget: TestTheSpire, 0.1.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TestTheSpire@0.1.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TestTheSpire&version=0.1.7
                    
Install as a Cake Addin
#tool nuget:?package=TestTheSpire&version=0.1.7
                    
Install as a Cake Tool

TestTheSpire

NuGet

TestTheSpire 是一个用于 Slay the Spire 2 的测试框架。利用这个框架,可以在纯命令行环境下,不通过 steam 启动游戏直接进行测试。这使得通过 AI 完成完整的 代码编写->测试->Review 回环成为可能,从而提升 AI 代码编写的效率与可靠性,使得长时间执行卡牌编写任务成为可能。

这个项目依赖本机 STS2 本体。编译时会引用 sts2.dllGodotSharp.dll,运行测试时会启动 STS2 可执行文件,并使用 headless 参数进入测试流程。

English Readme: README.en.md

哪些项目使用了 TestTheSpire

如何在自己的项目中使用 TestTheSpire

目前经过验证的环境包括 WSL2/Linux 下,在 Windows 下理论上可以运行,不过需要一些不同的配置,我们正努力增加支持,请耐心等待。

你可能需要了解如何使用 Steamcmd 在 linux 环境下获取 Slay the spire 2

先准备一个独立的测试项目,例如 YourMod.Tests/YourMod.Tests.csproj。测试项目通常引用被测 mod 项目,再通过 NuGet 引入 TestTheSpire:

dotnet add YourMod.Tests/YourMod.Tests.csproj package TestTheSpire --version 0.1.7

一个最小项目文件可以这样写:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <AssemblyName>yourmod_tests</AssemblyName>
    <RootNamespace>YourMod.Tests</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="../yourmod.csproj" />
    <PackageReference Include="TestTheSpire" Version="0.1.7" />
  </ItemGroup>
</Project>

本地验证还没有发布的 TestTheSpire 包时,先在 TestTheSpire 仓库执行 dotnet pack,再让测试项目从本地包目录 restore:

<RestoreSources>../TestTheSpire/artifacts/packages;$(RestoreSources)</RestoreSources>

测试程序集需要一个 STS2 mod initializer。这个 initializer 把当前测试程序集交给 TestTheSpire,xUnit fact 的发现和执行都会从这个程序集开始:

using System.Reflection;
using MegaCrit.Sts2.Core.Logging;
using MegaCrit.Sts2.Core.Modding;
using TestTheSpire;

namespace YourMod.Tests;

[ModInitializer("Init")]
public static class Entry
{
    public static void Init()
    {
        CombatTestBootstrap.Initialize(Assembly.GetExecutingAssembly(), new CombatTestOptions
        {
            LogPrefix = "yourmod.Tests"
        });

        Log.Info("[yourmod.Tests] Mod initialized");
    }
}

默认情况下,没有执行任何战斗 GameAction 的测试会立即失败,因为这种测试可能令 STS2 的 headless 清理流程崩溃。只进行模型或资源断言的测试可以启用清理 No-op;TestTheSpire 会在测试结束时自动插入一个仅供本地 runner 使用的动作:

CombatTestBootstrap.Initialize(Assembly.GetExecutingAssembly(), new CombatTestOptions
{
    LogPrefix = "yourmod.Tests",
    ZeroActionBehavior = ZeroActionTestBehavior.EnqueueCleanupNoOp
});

项目保留静态 manifest 时,把 TestTheSpire 放在被测 mod 之前。STS2 loader 会按依赖顺序加载 xunit.v3.assertTestTheSpire、被测 mod、测试 mod:

{
  "id": "yourmod_tests",
  "name": "yourmod.Tests",
  "author": "your team",
  "description": "Headless combat tests for yourmod.",
  "version": "0.1.7",
  "min_game_version": "0.111.0",
  "has_pck": false,
  "has_dll": true,
  "dependencies": [
    { "id": "TestTheSpire", "min_version": "0.1.7" },
    { "id": "yourmod", "min_version": null }
  ],
  "affects_gameplay": true
}

也可以让 MSBuild 在 CopySts2TestPayload 阶段生成测试 manifest。生成逻辑会读取 $(Sts2TestModId)$(Sts2MainModId) 和测试程序集名。

配置 STS2 路径

运行测试时传入 STS2 根目录:

dotnet msbuild YourMod.Tests/YourMod.Tests.csproj \
  -restore \
  -t:ListSts2Tests \
  -p:Sts2Path=/home/me/games/slay-the-spire-2

标准 Steam 安装通常可以让项目里的 GameFolder.props 推导路径。团队里常见的断点在路径不一致:有人把 STS2 放在独立磁盘,有人用解包目录跑测试,CI 也可能只挂载游戏目录。遇到这种情况,直接传属性最清楚:

dotnet msbuild YourMod.Tests/YourMod.Tests.csproj \
  -restore \
  -t:RunSts2Tests \
  -p:Sts2Path=/home/me/games/slay-the-spire-2

CI 镜像如果只挂载了数据目录,可以直接传 Sts2DataDir。本地如果需要指定可执行文件或 mods 目录,可以传 Sts2ExecutableSts2ModsDir

运行测试

只列出测试名,适合确认 STS2 已经正确加载测试 mod:

dotnet msbuild YourMod.Tests/YourMod.Tests.csproj \
  -restore \
  -t:ListSts2Tests \
  -p:Sts2Path=/home/me/games/slay-the-spire-2

开发者正在改一张卡时,通常先跑一个筛选测试。失败输出会停在具体 fact,MR 评审也能看到这次改动影响的是哪条战斗路径:

dotnet msbuild YourMod.Tests/YourMod.Tests.csproj \
  -restore \
  -t:RunSts2Tests \
  -p:Sts2Path=/home/me/games/slay-the-spire-2 \
  -p:Sts2TestArgs=--sts2-test-filter=Strike_deals_six_damage

常用 target:

  • CopySts2TestPayload:构建测试程序集,写出测试 mod payload。
  • InstallSts2TestMod:把 TestTheSpirexunit.v3.assert 和测试 mod 复制到 STS2 mods 目录。
  • ListSts2Tests:启动 headless STS2,打印发现到的 xUnit facts。
  • RunSts2Tests:启动 headless STS2,执行匹配到的测试。

ListSts2TestsRunSts2Tests 会把 STS2 的完整 stdout/stderr 写到 Sts2TestLogDir 下的 list.logrun.log。默认目录是 /tmp/sts2-combat-tests/<test-mod-id>/logs,MSBuild stdout 只回放测试开始 marker 之后的输出;启动阶段噪声需要看完整日志。

默认 settings 写到 /tmp/sts2-combat-tests/<test-mod-id>。CI 里多条 job 同时跑测试时,用 Sts2TestXdgDataHome 隔离 profile 和 settings:

dotnet msbuild YourMod.Tests/YourMod.Tests.csproj \
  -restore \
  -t:RunSts2Tests \
  -p:Sts2Path=/home/ci/sts2 \
  -p:Sts2TestXdgDataHome=/tmp/sts2-tests/$CI_JOB_ID

样例用例

下面的测试启动一场 Ironclad 对 Big Dummy 的战斗,把 Strike 加到手牌,打出后检查敌人 HP。这个用例适合作为接入后的第一条 smoke test:如果它失败,问题大概率在 STS2 路径、mod manifest、依赖加载顺序或 initializer。

using MegaCrit.Sts2.Core.Models.Cards;
using MegaCrit.Sts2.Core.Models.Characters;
using MegaCrit.Sts2.Core.Models.Monsters;
using TestTheSpire;
using Xunit;

namespace YourMod.Tests;

public sealed class StrikeTests : CombatTestSuite
{
    protected override void ConfigureBattle(CombatTestBattleBuilder battle)
    {
        battle
            .Player<Ironclad>()
            .AddEnemy<BigDummy>()
            .WithSeed("strike-sample");
    }

    [Fact]
    public async Task Strike_deals_six_damage()
    {
        var enemy = EnemyAt(0);
        var hpBefore = enemy.CurrentHp;
        var strike = await AddToHand<StrikeIronclad>();

        await Play(strike, enemy);

        Assert.Equal(hpBefore - 6, enemy.CurrentHp);
    }
}

mod 卡牌测试沿用同一条路径:准备战斗,注入目标卡牌,记录敌人 HP、玩家格挡或牌堆数量,打出卡牌,等待 action queue 清空后断言状态变化。这样失败日志会落在具体战斗状态上,开发者可以直接回到卡牌实现或 hook 里修正。

CardTestAssertions 还提供 PowerAmount<TPower>AssertPowerAmount<TPower>AssertCurrentPortraitPathsExistProjectFileExists。资源路径检查会优先使用 Godot ResourceLoader;也可以给后两个方法传入显式项目目录,适配尚未打进 PCK 的测试资源。ModelHookCompatExtensions 同时提供从旧式 BeforeTurnEndAfterTurnEnd 到当前 side-turn hook 的兼容调用。

维护 TestTheSpire

这一节面向维护框架和发布 NuGet 包的人。

目录

  • TestTheSpire.csproj:NuGet 包项目。
  • Framework/:mod 初始化、xUnit 断言运行器、战斗上下文和测试辅助方法。
  • buildTransitive/:通过 PackageReference 自动导入到测试项目的 MSBuild targets。
  • GameFolder.props:TestTheSpire 仓库内的 STS2 路径推导。个人机器路径可以写到 LocalSettings.props
  • LICENSE:LGPL-3.0 license 文本。

构建和打包

在 TestTheSpire 仓库根目录执行:

dotnet build TestTheSpire.csproj -c Debug

dotnet pack TestTheSpire.csproj -c Release

NuGet 包会生成到:

artifacts/packages/TestTheSpire.0.1.7.nupkg

发布到 nuget.org:

dotnet nuget push artifacts/packages/TestTheSpire.0.1.7.nupkg \
  --api-key "$NUGET_API_KEY" \
  --source https://api.nuget.org/v3/index.json \
  --skip-duplicate

NuGet 版本号发布后无法覆盖。下一次发布前先修改 TestTheSpire.csproj 里的 <Version>

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.7 101 8/18/2026
0.1.6 100 8/8/2026
0.1.5 96 8/8/2026
0.1.4 127 7/4/2026
0.1.3 126 6/24/2026
0.1.0 117 6/4/2026