# 一、基础

  • 单元测试:单元测试_百度百科
  • 官方文档:自动化技术指南

# 二、UE5 简单小实验

  • 根据官方文档实现的复杂测试代码
    #include "Misc/AutomationTest.h"
    
    /// 复杂测试
    IMPLEMENT_CUSTOM_COMPLEX_AUTOMATION_TEST(FAPTest_VehicleMoveToLocation, FAutomationTestBase, "AutoPilot.Vehicle.MoveToLocation"/*测试分类、层级、名称*/, EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter);
    
    /// 这里会在编辑器中创建两个测试,并且传递不同参数给RunTest执行测试,测试代码相同。
    /// 所以复杂测试适合 批量测试内容相同 而参数不同的测试,比如:测试加载所有地图、测试不同NPC对玩家造成的伤害...。
    void FAPTest_VehicleMoveToLocation::GetTests(TArray<FString>& OutBeautifiedNames, TArray<FString>& OutTestCommands) const
    {
    	/////////////////////////////////第一个测试///////////////////////////////////
    	/// 编辑器中显示的“叶子”节点名称
    	OutBeautifiedNames.Add(TEXT("Vehicle_0"));
    	/// 传给RunTest的参数
    	OutTestCommands.Add(TEXT("Parameter_"));
    
    	/////////////////////////////////第二个测试///////////////////////////////////
    	OutBeautifiedNames.Add(TEXT("Vehicle_1"));
    	OutTestCommands.Add(TEXT("Parameter_1"));
    }
    
    /// 根据选择的测试,从OutTestCommands数组中取出对应参数执行测试。
    bool FAPTest_VehicleMoveToLocation::RunTest(const FString& Parameters)
    {
    	/// 返回true代表测试成功,否则测试失败
    	return true;
    }
    
  • 辑器中的可视化工具:Session Frontend
    Session Frontend
  • 命令行触发自动化测试: Automation RunTests AutoPilot.Vehicle.MoveToLocation
    • 自动化测试是虚幻架构上层的实现,与平台无关!所以不论是 PS4、XboxOne、Switch 还是 Steam,只要启动游戏,传输自动化测试命令就可以运行自动化测试!

# 引擎源码解析 - 自动化测试

  • 自动化测试宏
    IMPLEMENT_SIMPLE_AUTOMATION_TEST (TClass, PrettyName, TFlags), 其他复杂的测试宏同理!
    #define IMPLEMENT_SIMPLE_AUTOMATION_TEST( TClass, PrettyName, TFlags ) \
    		IMPLEMENT_SIMPLE_AUTOMATION_TEST_PRIVATE(TClass, FAutomationTestBase, PrettyName, TFlags, __FILE__, __LINE__) \
    		namespace\
    		{\
    						/// 创建了该测试的实例
    			TClass TClass##AutomationTestInstance( TEXT(#TClass) );\
    		}
    
    • 该宏里面包裹另一个宏:IMPLEMENT_SIMPLE_AUTOMATION_TEST_PRIVATE,它会创建一个测试对象 FAutomationTestBase 的子类 TClass(宏的第一个参数)
      //#define IMPLEMENT_SIMPLE_AUTOMATION_TEST_PRIVATE( TClass, TBaseClass, PrettyName, TFlags, FileName, LineNumber ) \
      	class TClass : public TBaseClass \
      	{ \
      	public: \
      		/// ... 省略
      	protected: \
      				/// 得到测试名称和测试参数 
      		virtual void GetTests(TArray<FString>& OutBeautifiedNames, TArray <FString>& OutTestCommands) const override \
      		{ \
      			OutBeautifiedNames.Add(PrettyName); \
      			OutTestCommands.Add(FString()); \
      		} \
      				/// 你的测试代码
      		virtual bool RunTest(const FString& Parameters) override; \
      		virtual FString GetBeautifiedTestName() const override { return PrettyName; } \
      	};
      
  • 潜在命令
    • 宏定义潜在命令:
      DEFINE_LATENT_AUTOMATION_COMMAND/ 零个参数 /;
      DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER/ 一个参数 /;
      .... / 更多参数 /;

    • 使用潜在命令,让其排队等候执行:ADD_LATENT_AUTOMATION_COMMAND

    • 潜在命令的概念

      • 官方文档:在 "RunTest" 期间,系统可以对潜在命令进行排队,从而使代码的某些部分跨多个帧运行。若要创建一个潜在操作,则必须通过 "DEFINE_LATENT_AUTOMATION_COMMAND" 宏来定义它。
      • 我的理解:很多测试不是单次直行就能完成,为了跨多帧执行而设计的潜在命令。如果你在 “RunTest” 的时候使用这些命令,那么他们会在 FAutomationTestFramework::LatentCommands 排队(队列:先进先出,依次执行),每帧都会取出队头命令执行,直到该命令被完成,才会执行下一个命令!(小心队列被阻塞,避免一个永远完不成的命令在队列中)。
    • 潜在命令的使用

      /// 定义只有一个参数的潜在命令
      DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(FAPTestCommand_MoveVehicle, FVector, _TargetLocation);
      bool FAPTestCommand_MoveVehicle::Update()
      {
      	/// 移动玩家代码...
      
      	if (/*到达指定位置*/)
      	{
      		/// 命令完成
      		/// LatentCommands 出队当前命令,下一帧执行下一条命令
      		return true;
      	}
      
      	/// 命令没完成,下一帧继续
      	return false;
      }
      
      bool FAPTest_VehicleMoveToLocation::RunTest(const FString& Parameters)
      {
      		/// 使用潜在命令
      	ADD_LATENT_AUTOMATION_COMMAND(FAPTestCommand_MoveVehicle(FVector(0.f, 0.f, 0.f)));
      	ADD_LATENT_AUTOMATION_COMMAND(/*其他命令*/);
      	return true;
      }
      
    • 潜在命令源码

      /// 定义
      #define DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(CommandName,ParamType,ParamName)	\
      /// 申明了一个潜在命令类,实现接口IAutomationLatentCommand 
      class CommandName : public IAutomationLatentCommand \
      	{ \
      	public: \
      	CommandName(ParamType InputParam) \
      	: ParamName(InputParam) \
      		{} \
      		virtual ~CommandName() \
      		{} \
      				/// 跨多帧执行命令
      		virtual bool Update() override; \
      	private: \
      		/// 参数会申明为成员变量
      	ParamType ParamName; \
      }
      
      
      //macro to simply the syntax for enqueueing a latent command
      /// 这里就是入队FAutomationTestFramework::LatentCommands,
      /// 然后每帧在ExecuteLatentCommands函数中,取队头执行
      #define ADD_LATENT_AUTOMATION_COMMAND(ClassDeclaration) FAutomationTestFramework::Get().EnqueueLatentCommand(MakeShareable(new ClassDeclaration));
      

# 四、架构设计类图

  • AutomationTest Framework
    Automation Test Framwork
  • MessageBus
    Message Bus