Advanced Usage
This guide covers some of the more advanced features, patterns and configuration options of AltTester Unity SDK.
Custom input vs. regular input
AltTester’s custom input is active, by default, in any instrumented build. This means that certain input related actions (the ones that are part of Unity’s Input
class) will be inactive for regular input (the device’s input). Because of this, pressing a key from the keyboard for example will not have any effect on the game. However, the simulated input from the tests, like the PressKey
command, will be able to manipulate the object within the scene. While the custom input is active, the icon from the right bottom corner is green. You can change this behaviour by clicking on the AltTester’s icon and unchecking the box with the Custom Input
message. Now the icon will turn darker, signaling that the regular input is active. In this state, you can interfere with the object from the game using the keyboard or other input. Keep in mind that, input actions from the AltTester Desktop won’t have any effect while regular input is active. At the same time, if you want to run some automated tests, the custom input will be activated automatically for you.
Build games from the command line
To build your Unity application from command line you need a static method in
your project that handles the build logic. To instrument your Unity application
with AltTester Unity SDK, your build method must define ALTTESTER
scripting
symbol and must insert AltTester Prefab in the first scene of the game.
Depending on your project’s setup, there are two ways in which games can be built from the command line:
Note
AltTester Unity SDK does not work by default in release mode. If you instrument your game in release mode, AltTester Prefab self removes from the scenes and the socket server does not start. Best case practice is to customize your build script to insert AltTester Prefab only in Debug mode.
If you do want to use AltTester Unity SDK in release mode see Using AltTester Unity SDK in Release mode section.
1. If you already have a custom build method for your game
If you already have a custom build method for your game, you can add the following lines to your build method. Also, the BuildPlayerOptions should check for BuildOptions.Development and BuildOptions.IncludeTestAssemblies.
var buildTargetGroup = BuildTargetGroup.Android;
AltBuilder.AddAltTesterInScriptingDefineSymbolsGroup(buildTargetGroup);
if (buildTargetGroup == UnityEditor.BuildTargetGroup.Standalone) {
AltBuilder.CreateJsonFileForInputMappingOfAxis();
}
var instrumentationSettings = new AltInstrumentationSettings();
AltBuilder.InsertAltInScene(FirstSceneOfTheGame, instrumentationSettings);
Note
Change buildTargetGroup
above to the target group for which you are
building.
2. If you create a new custom build method for your game
The following example script can be used. It sets all the project settings needed and uses the same two important lines from point 1 above.
This example method is configured for the Android platform, so make sure to update it based on your target platform.
static void BuildFromCommandLine () {
try {
BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
buildPlayerOptions.scenes = new string[] {
"Assets/AltTester/Examples/Scenes/Scene 1 AltDriverTestScene.unity",
"Assets/AltTester/Examples/Scenes/Scene 2 Draggable Panel.unity",
"Assets/AltTester/Examples/Scenes/Scene 3 Drag And Drop.unity",
"Assets/AltTester/Examples/Scenes/Scene 4 No Cameras.unity",
"Assets/AltTester/Examples/Scenes/Scene 5 Keyboard Input.unity",
"Assets/AltTester/Examples/Scenes/Scene 7 Drag And Drop NIS.unity",
"Assets/AltTester/Examples/Scenes/Scene 7 New Input System Actions.unity",
"Assets/AltTester/Examples/Scenes/Scene 8 Draggable Panel NIP.unity",
"Assets/AltTester/Examples/Scenes/Scene 9 NIS.unity",
"Assets/AltTester/Examples/Scenes/Scene 10 Sample NIS.unity,
"Assets/AltTester/Examples/Scenes/Scene 11 ScrollView Scene.unity",
"Assets/AltTester/Examples/Scenes/Scene6.unity"
};
buildPlayerOptions.locationPathName = "sampleGame.apk";
buildPlayerOptions.target = BuildTarget.Android;
buildPlayerOptions.options = BuildOptions.Development | BuildOptions.IncludeTestAssemblies | BuildOptions.AutoRunPlayer;
// Setup for AltTester
var buildTargetGroup = BuildTargetGroup.Android;
AltBuilder.AddAltTesterInScriptingDefineSymbolsGroup (buildTargetGroup);
if (buildTargetGroup == UnityEditor.BuildTargetGroup.Standalone)
AltBuilder.CreateJsonFileForInputMappingOfAxis();
var instrumentationSettings = new AltInstrumentationSettings();
AltBuilder.InsertAltInScene (buildPlayerOptions.scenes[0], instrumentationSettings);
var results = BuildPipeline.BuildPlayer (buildPlayerOptions);
AltBuilder.RemoveAltTesterFromScriptingDefineSymbols (BuildTargetGroup.Android);
} catch (Exception exception) {
Debug.LogException (exception);
}
}
The following command is used to call the build method:
<UnityPath>/Unity -projectPath $CI_PROJECT_DIR -executeMethod BuilderClass.BuildFromCommandLine -logFile logFile.log -quit
You can find more information about the build command and arguments here.
Note
After building from the command line you can run the tests by using the commands from the next section.
Run tests from the command line
In order to run tests from the command line you can use the following example commands:
Available Alt command line arguments:
-testsClass
- runs tests from given class/classes
Example command running tests from a single test class:
<UnityPath>/Unity -projectPath $PROJECT_DIR -executeMethod Altom.AltTesterEditor.AltTestRunner.RunTestFromCommandLine -testsClass MyTestClass -logFile logFile.log -batchmode -quit
Example command running tests from two test classes:
<UnityPath>/Unity -projectPath $PROJECT_DIR -executeMethod Altom.AltTesterEditor.AltTestRunner.RunTestFromCommandLine -testsClass MyTestClass1 MyTestClass2 -logFile logFile.log -batchmode -quit
-tests
- runs given test/tests
Example command running a single test:
<UnityPath>/Unity -projectPath $PROJECT_DIR -executeMethod Altom.AltTesterEditor.AltTestRunner.RunTestFromCommandLine -tests MyTestClass.MyTestName -logFile logFile.log -batchmode -quit
Example command running two tests:
<UnityPath>/Unity -projectPath $PROJECT_DIR -executeMethod Altom.AltTesterEditor.AltTestRunner.RunTestFromCommandLine -tests MyTestClass1.MyTestName1 MyTestClass2.MyTestName2 -logFile logFile.log -batchmode -quit
-testsAssembly
- runs tests from given assembly/assemblies
Example command running all tests from given assembly:
<UnityPath>/Unity -projectPath $PROJECT_DIR -executeMethod Altom.AltTesterEditor.AltTestRunner.RunTestFromCommandLine -testsAssembly MyAssembly -logFile logFile.log -batchmode -quit
Example command running tests from two assemblies:
<UnityPath>/Unity -projectPath $PROJECT_DIR -executeMethod Altom.AltTesterEditor.AltTestRunner.RunTestFromCommandLine -testsAssembly MyAssembly1 MyAssembly2 -logFile logFile.log -batchmode -quit
-reportPath
- the xml test report will be generated here
<UnityPath>/Unity -projectPath $PROJECT_DIR -executeMethod Altom.AltTesterEditor.AltTestRunner.RunTestFromCommandLine -tests MyFirstTest.TestStartGame -reportPath $PROJECT_DIR/testReport.xml -logFile logFile.log -batchmode -quit
mvn test
Using the unittest
module:
python -m unittest <name_of_your_test_file.py>
Using the pytest
package:
pytest <name_of_your_test_file.py>
Run tests on a Continuous Integration Server
Instrument your game build with AltTester Unity SDK from Unity or by building from the command line.
Start the game build on a device.
Run your tests - see commands in the “Run tests from the command line” section.
An example CI configuration file can be viewed in the GitLab repository.
What is port forwarding and when to use it
Port forwarding, or tunneling, is the behind-the-scenes process of intercepting data traffic headed for a computer’s IP/port combination and redirecting it to a different IP and/or port.
When you run your game instrumented with AltTester Unity SDK, on a device, you need to tell your AltDriver how to connect to it.
Port forwarding can be set up either through a command line command or in the test code by using the methods available in Alt classes.
The following are some cases when Port Forwarded is needed:
How to setup port forwarding
Port forwarding can be set up in three ways:
through a command line command (using ADB/IProxy)
in the test code by using the methods available in Alt classes
from AltTester Editor - Port Forwarding Section
All methods listed above require that you have ADB or IProxy installed.
For installing ABD, check this article for more information on ADB.
For installing IProxy brew install libimobiledevice
. (Requires IProxy 2.0.2)
Forward the port using the following command:
adb [-s UDID] forward tcp:local_port tcp:device_port
Forward using AltTester Editor: click on the refresh button in the Port Forwarding section in the Editor to see connected devices and then select the device to forward.
Forward the port using the following command:
iproxy LOCAL_PORT DEVICE_PORT -u [UDID]
Forward using AltTester Editor: click on the refresh button in the Port Forwarding section in the Editor to see connected devices and then select the device to forward.
Use the following static methods (from the AltPortForwarding
class) in your test file:
ForwardAndroid (int localPort = 13000, int remotePort = 13000, string deviceId = “”, string adbPath = “”)
RemoveForwardAndroid (int localPort = 13000, string deviceId = “”, string adbPath = “”)
Example test file:
using NUnit.Framework; using Altom.AltDriver; public class MyFirstTest { private AltDriver altDriver; [OneTimeSetUp] public void SetUp() { AltPortForwarding.ForwardAndroid(); altDriver = new AltDriver(); } [OneTimeTearDown] public void TearDown() { altDriver.Stop(); AltPortForwarding.RemoveForwardAndroid(); } [Test] public void TestStartGame() { altDriver.LoadScene("Scene 2 Draggable Panel"); altDriver.FindObject(By.NAME, "Close Button").Tap(); altDriver.FindObject(By.NAME, "Button").Tap(); var panelElement = altDriver.WaitForObject(By.NAME, "Panel"); Assert.IsTrue(panelElement.enabled); } }
Use the following static methods (from the AltPortForwarding class) in your test file:
ForwardIos(int localPort = 13000, int remotePort = 13000, string deviceId = “”, string iproxyPath = “”)
KillAllIproxyProcess()
Example test file:
using NUnit.Framework; using Altom.AltDriver; public class MyFirstTest { private AltDriver altDriver; [OneTimeSetUp] public void SetUp() { AltPortForwarding.ForwardIos(); altDriver = new AltDriver(); } [OneTimeTearDown] public void TearDown() { altDriver.Stop(); AltPortForwarding.KillAllIproxyProcess(); } [Test] public void TestStartGame() { altDriver.LoadScene("Scene 2 Draggable Panel"); altDriver.FindObject(By.NAME, "Close Button").Tap(); altDriver.FindObject(By.NAME, "Button").Tap(); var panelElement = altDriver.WaitForObject(By.NAME, "Panel"); Assert.IsTrue(panelElement.enabled); } }
Use the following static methods (from the AltPortForwarding class) in your test file:
forwardAndroid (int localPort = 13000, int remotePort = 13000, string deviceId = “”, string adbPath = “”)
removeForwardAndroid (int localPort = 13000, string deviceId = “”, string adbPath = “”)
Example test file:
import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import com.alttester.AltPortForwarding; import com.alttester.AltDriver; import com.alttester.AltObject; import com.alttester.Commands.FindObject.AltFindObjectsParameters; import com.alttester.Commands.FindObject.AltWaitForObjectsParameters; import java.io.IOException; public class myFirstTest { private static AltDriver altDriver; @BeforeClass public static void setUp() throws IOException { AltPortForwarding.forwardAndroid(); altDriver = new AltDriver(); } @AfterClass public static void tearDown() throws Exception { altDriver.stop(); AltPortForwarding.removeForwardAndroid(); } @Test public void openClosePanelTest() { altDriver.loadScene("Scene 2 Draggable Panel"); AltFindObjectsParameters altFindObjectsParametersCamera = new AltFindObjectsParameters.Builder( AltDriver.By.PATH, "//Main Camera") .build(); AltObject camera = altDriver.findObject(altFindObjectsParametersCamera); AltFindObjectsParameters closeButtonObjectsParameters = new AltFindObjectsParameters.Builder( AltDriver.By.NAME, "Close Button") .withCamera(AltDriver.By.ID, String.valueOf(camera.id)) .build(); altDriver.findObject(closeButtonObjectsParameters).tap(); AltFindObjectsParameters buttonObjectsParameters = new AltFindObjectsParameters.Builder( AltDriver.By.NAME, "Button") .withCamera(AltDriver.By.ID, String.valueOf(camera.id)) .build(); altDriver.findObject(buttonObjectsParameters).tap(); AltFindObjectsParameters panelObjectsParameters = new AltFindObjectsParameters.Builder( AltDriver.By.NAME, "Panel") .withCamera(AltDriver.By.ID, String.valueOf(camera.id)) .build(); AltWaitForObjectsParameters panelWaitForObjectsParameters = new AltWaitForObjectsParameters.Builder( panelObjectsParameters).build(); AltObject panelElement = altDriver.waitForObject(panelWaitForObjectsParameters); Assert.assertTrue(panelElement.isEnabled()); } }
Use the following static methods (from the AltPortForwarding class) in your test file:
forwardIos (int localPort = 13000, int remotePort = 13000, string deviceId = “”, string iproxyPath = “”)
killAllIproxyProcess ()
Example test file:
import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import com.alttester.AltPortForwarding; import com.alttester.AltDriver; import com.alttester.AltObject; import com.alttester.Commands.FindObject.AltFindObjectsParameters; import com.alttester.Commands.FindObject.AltWaitForObjectsParameters; import java.io.IOException; public class myFirstTest { private static AltDriver altDriver; @BeforeClass public static void setUp() throws IOException { AltPortForwarding.forwardIos(); altDriver = new AltDriver(); } @AfterClass public static void tearDown() throws Exception { altDriver.stop(); AltPortForwarding.killAllIproxyProcess(); } @Test public void openClosePanelTest() { altDriver.loadScene("Scene 2 Draggable Panel"); AltFindObjectsParameters altFindObjectsParametersCamera = new AltFindObjectsParameters.Builder( AltDriver.By.PATH, "//Main Camera") .build(); AltObject camera = altDriver.findObject(altFindObjectsParametersCamera); AltFindObjectsParameters closeButtonObjectsParameters = new AltFindObjectsParameters.Builder( AltDriver.By.NAME, "Close Button") .withCamera(AltDriver.By.ID, String.valueOf(camera.id)) .build(); altDriver.findObject(closeButtonObjectsParameters).tap(); AltFindObjectsParameters buttonObjectsParameters = new AltFindObjectsParameters.Builder( AltDriver.By.NAME, "Button") .withCamera(AltDriver.By.ID, String.valueOf(camera.id)) .build(); altDriver.findObject(buttonObjectsParameters).tap(); AltFindObjectsParameters panelObjectsParameters = new AltFindObjectsParameters.Builder( AltDriver.By.NAME, "Panel") .withCamera(AltDriver.By.ID, String.valueOf(camera.id)) .build(); AltWaitForObjectsParameters panelWaitForObjectsParameters = new AltWaitForObjectsParameters.Builder( panelObjectsParameters).build(); AltObject panelElement = altDriver.waitForObject(panelWaitForObjectsParameters); Assert.assertTrue(panelElement.isEnabled()); } }
Use the following static methods (from the AltPortForwarding class) in your test file:
forward_android (local_port = 13000, device_port = 13000, device_id = “”)
remove_forward_android (local_port = 13000, device_id = “”)
Example test file:
import unittest from alttester import * class MyFirstTest(unittest.TestCase): altdriver = None @classmethod def setUpClass(cls): AltPortForwarding.forward_android() cls.altdriver = AltDriver() @classmethod def tearDownClass(cls): cls.altdriver.stop() AltPortForwarding.remove_forward_android() def test_open_close_panel(self): self.altdriver.load_scene("Scene 2 Draggable Panel") self.altdriver.find_object(By.NAME, "Close Button").tap() self.altdriver.find_object(By.NAME, "Button").tap() panel_element = self.altdriver.wait_for_object(By.NAME, "Panel") self.assertTrue(panel_element.enabled)
Use the following static methods (from the AltPortForwarding class) in your test file:
forward_ios (local_port = 13000, device_port = 13000, device_id = “”)
kill_all_iproxy_process()
Example test file:
import unittest from alttester import * class MyFirstTest(unittest.TestCase): altdriver = None @classmethod def setUpClass(cls): AltPortForwarding.forward_ios() cls.altdriver = AltDriver() @classmethod def tearDownClass(cls): cls.altdriver.stop() AltPortForwarding.kill_all_iproxy_process() def test_open_close_panel(self): self.altdriver.load_scene("Scene 2 Draggable Panel") self.altdriver.find_object(By.NAME, "Close Button").tap() self.altdriver.find_object(By.NAME, "Button").tap() panel_element = self.altdriver.wait_for_object(By.NAME, "Panel") self.assertTrue(panel_element.enabled)
Note
The default port on which the AltTester Unity SDK is running is 13000. Port can be changed when making a new game build or make use of port forwarding if needed.
Connect to AltTester Unity SDK running inside the game
There are multiple scenarios on how to connect to the AltTester Unity SDK running inside a game:
Connect to the game running on the same machine as the test code
Connect to the game running on a USB connected device by using Port Forwarding.
Connect to the device running the game by using an IP address
Connect to multiple devices running the game by using Port Forwarding.
Connect to multiple builds of the application running on the same device
Connect to the game running on the same machine as the test code
In this case Port Forwarding is not needed as both the game and tests are using localhost (127.0.0.1) connection and the default 13000 port.
Connect to the game running on a USB connected device
If the device running the game is connected through a USB connection, commands sent to localhost port 13000 can be automatically forwarded to the device.
In this scenario you can use Port Forwarding to enable AltDriver to connect to the device via localhost.
Check Port Forwarding for more details about Port Forwarding and Setup Port Forwarding section on how to make the setup.
Connect to the device running the game by using an IP address
You can connect directly through an IP address if the port the instrumented Unity App is listening on is available and the IP address is reachable. It is recommended to use Port Forwarding since IP addresses could change and would need to be updated more frequently.
The following command can be used to connect to the running instrumented Unity App:
altDriver = new AltDriver ("deviceIp", 13000);
altDriver = new AltDriver ("deviceIp", 13000, true);
cls.altDriver = AltDriver(host='deviceIp', port=13000)
Connect to multiple devices running the game
For two devices you have to do the same steps above, by connecting through port forwarding twice.
So, in the end, you will have:
2 devices, each with one instrumented Unity App
1 computer with two AltDrivers
Then, in your tests, you will send commands from each of the two AltDrivers.
The same happens with n devices, repeat the steps n times.
Connect to multiple builds of the application running on the same device
If you want to run two builds on the same device you will need to change the AltTester Unity SDK Port during instrumentation.
For example, you will instrument a game with AltTester Unity SDK to listen on port 13001 and another one to listen on port 13002.
Then in your tests you will need to create two AltDriver instances, one for each of the configured ports.
Important
On mobile devices, AltDriver can only interact with a single game at a time and the game needs to be in focus.
On Android/iOS only one application is in focus at a time so you need to switch (in code) between the applications if using two drivers at the same time. This applies even when using split screen mode.
You can change the port for your game build from the AltTester Editor window inside your Unity project.
Note
After you have done the AltTester Unity SDK Port forwarding or connected to the AltDriver directly, you can use it in your tests to send commands to the server and receive information from the game.
Using AltTester Unity SDK in Release mode
By default AltTester Unity SDK does not run in release mode. We recommended that you do not instrument your Unity application in release mode with AltTester Unity SDK. That being said, if you do want to instrument your application in release mode, you need to uncheck RunOnlyInDebugMode
flag on AltRunnerPrefab inside AltTester Unity SDK asset folder AltTester/Prefab/AltRunnerPrefab.prefab
Logging
There are two types of logging that can be configured in AltTester Unity SDK. The logs from AltDriver (from the tests) and the logs from the AltTester Unity SDK (from the instrumented Unity application)
Note
From version 1.7.0 on logs from Server are referred to as logs from Tester.
AltTester Unity SDK logging
Logging inside the instrumented Unity application is handled using a custom NLog LogFactory. The Server LogFactory can be accessed here: Altom.AltTester.Logging.ServerLogManager.Instance
There are two logger targets that you can configure on the server:
FileLogger
UnityLogger
Logging inside the instrumented app can be configured from the driver using the SetServerLogging command:
altDriver.SetServerLogging(AltLogger.File, AltLogLevel.Off);
altDriver.SetServerLogging(AltLogger.Unity, AltLogLevel.Info);
altDriver.setServerLogging(AltLogger.File, AltLogLevel.Off);
altDriver.setServerLogging(AltLogger.Unity, AltLogLevel.Info);
altDriver.set_server_logging(AltLogger.File, AltLogLevel.Off);
altDriver.set_server_logging(AltLogger.Unity, AltLogLevel.Info);
AltDriver logging
Logging on the driver is handled using NLog
in C#, loguru
in python and log4j
in Java. By default logging is disabled in the driver (tests). If you want to enable it you can set the enableLogging
in AltDriver
constructor.
Logging is handled using a custom NLog LogFactory. The Driver LogFactory can be accessed here: Altom.AltDriver.Logging.DriverLogManager.Instance
There are three logger targets that you can configure on the driver:
FileLogger
UnityLogger //available only when runnning tests from Unity
ConsoleLogger //available only when runnning tests using the Nuget package
If you want to configure different level of logging for different targets you can use Altom.AltDriver.Logging.DriverLogManager.SetMinLogLevel(AltLogger.File, AltLogLevel.Info)
/* start AltDriver with logging disabled */
var altDriver = new AltDriver (enableLogging: false);
/* start AltDriver with logging enabled for Debug.Level; this is the default behaviour*/
var altDriver = new AltDriver (enableLogging: true);
/* disable AltDriver logging */
altDriver.SetLogging(enableLogging: false);
/* enable AltDriver logging */
altDriver.SetLogging(enableLogging: true);
/* set logging level to Info for File target */
Altom.AltDriver.Logging.DriverLogManager.SetMinLogLevel(AltLogger.File, AltLogLevel.Info);
Logging is handled via log4j. You can use log4j configuration files to customize your logging.
Setting the enableLogging in AltDriver initializes logger named ro.altom.alttester configured with two appenders, a file appender AltFileAppender and a console appender AltConsoleAppender
/* start AltDriver with logging enabled */
altDriver = new AltDriver("127.0.0.1", 13000, true);
/* disable logging for ro.altom.alttester logger */
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
config.getLoggerConfig("ro.altom.alttester").setLevel(Level.OFF);
ctx.updateLoggers();
Logging is handled via loguru.
Setting the enable_logging to False in AltDriver, all logs from alttester package are disabled.
# enable logging in driver:
loguru.logger.enable("alttester")
# disable logging in driver:
loguru.logger.disable("alttesterr")
Code Stripping
AltTester Unity SDK is using reflection in some of the commands to get information from the instrumented application. If you application is using IL2CPP scripting backend then it might strip code that you would use in your tests. If this is the case we recommend creating an link.xml
file. More information on how to manage code stripping and create an link.xml
file is found in Unity documentation