Continuous Integration with Unity
- 8 minutes read - 1498 wordsThis article gives an overview of the test framework in Unity and how to set up a continuous integration system with Unity and Jenkins. This article assumes you have some basic idea about unit testing and bash script.
What is Unity test and continuous integration
Unity test
Unity test is very similar to other unit testing frameworks. From Unity’s documentation, Unity actually uses the .Net unit testing NUnit library. The difference is that there are different modes that users can choose to run their tests in. Tests in edit mode are executed in the way as you would expect in other unit testing frameworks, but tests in play mode are executed in a bit more interesting way. Let’s take the test in the above documentation as an example:
public class SamplePlayModeTest {
[UnityTest]
public IEnumerator TestRigidbodyPhysics()
{
var myGameObject = new GameObject();
myGameObject.AddComponent<Rigidbody>();
var originalPosition = myGameObject.transform.position.y;
yield return new WaitForFixedUpdate();
// y should have changed because of gravity.
Assert.AreNotEqual(originalPosition, myGameObject.transform.position.y);
}
}
In this example, a new GameObject is created. After that, A Rigidbody component is added to that GameObject, which makes that GameObject be influenced by Unity physics engine. Because of the gravity, the originalPosition should not be equal to the new position after the FixedUpdate.
Continuous integration
After you have some tests, you may want to have a service that can automatically run those tests after each change you push to your git repository, and build the project for you. This automation process is so called continuous integration. In the rest of the article I will walk you through how to set up a continuous integration server with Jenkins. But first, we need to know a little more about Unity command line interface.
Getting familiar with Unity command line interface
Run Unity tests from command line interface
Being able to run Unity tests from command line is essential to build a continuous integration system. We will first demonstrate how to run Unity tests from command line.
First of all, it would be easier to run Unity from command line if we add an alis in bash profile. Add the following line to your ~/.bash_profile.
alias unity='/Applications/Unity/Unity.app/Contents/MacOS/Unity'
Now we are able to launch Unity from terminal by just typing unity
.
However, if we only type unity
, it will just start the Unity launcher, and we still need to manualy choose whether to create/open projects, and etc. Therefore, there are some command line augments we need to learn to launch Unity silently and run tests.
-batchmode
This is almost always needed. It will ensure no pop-up windows and eliminate human intervention.-projectPath <pathname>
Specify your project location.-logFile <pathname>
Specify where to write Unity Editor logs. We will mostly like need infomation in the log file when we build CI system.-runTests
Run Unity tests.-testResults <pathname>
Specify where to write Unity tests results. Again, We will mostly like need infomation in the result file when we build CI system.-testPlatform <name>
Specify the platform you want to run test on. Currently we will mainly useplaymode
andeditmode
.
These are about the minimum command line augments you need to know to run Unity tests from command line. A complete example would be the following:
unity -batchmode \
-runTests \
-projectPath $PROJECT_PATH \
-logFile $LOG_PATH \
-testResults $RESULTS_PATH \
-testPlatform $PLATFORM_NAMES
After that, we can check results from log file and test results file.
Build a Unity project from command line interface
Building a Unity project from command line interface is very similar to running Unity tests. Here we assume the project is relative simple and build settings are already configured in the Unity Editor. You can read more about complex builds that involve build script here and the example here.
The only change we need to make from the running test script is to replace all the test related arguments with build arguments. For example:
-buildOSXUniversalPlayer <pathname>
This will build the project into macOS executable. Building for other platforms are possible with similar arguments. Users can find the full list here.
A complete example would be the following:
unity -batchmode \
-projectPath $PROJECT_PATH \
-logFile $LOG_PATH \
-buildOSXUniversalPlayer $BUILD_PATH \
-quit
Set up continuous integration with Jenkins
Finally, it comes to setting up continuous integration with Jenkins server. So far, we’ve learned all the essential parts about what Unity test and continuous integration are, and how to run Unity tests and build Unity project using Bash from command line interface. Now we need to set up a Jenkins server to automate the entire process.
Install Jenkins
Jenkins is included in most of the package managers, so you should be able to install it easily. You can also download it from their website.
For macOS users who have Homebrew installed, you can simply run:
brew install jenkins
After installation, you can start the Jenkins server by running command jenkins
from terminal. Then you can go to localhost:8080
in your browser to initialize the Jenkins server. Make sure you have Github plugin selected and installed because we will connect this server to a Github repository later.
Create a new build item in Jenkins
After the initialization, you should be able to see the Jenkins dashboard. Click the New Item button on the left to create a new build item. This build item is the service that will automatically build our project after each change we make to our Github repository.
After click the New Item button, enter an item name, select Freestyle project and click OK. This should bring you to the configuration page.
In this configuration page, you will need to connect your Jenkins server to your Github repository and specify your build steps. Details on how to connect Jenkins server with Github repository can be found here and here. I will focus more on how to build the project after the Jenkins server successfully get the content from the Github repository.
Write a build script
After Jenkins server successfully pull your project from your Github repository, it needs to know how to build the project. Here we will use what we’ve learned about Unity command line interface, and write a build bash script to test and build our Unity project.
Suppose your project has a structure like this:
.
+-- build.sh # This is the build script we will be writing
+-- src/ # This is the your Unity project
+-- build/ # This folder contains your build results
+-- output/ # This folder contains the log files during the building process
We want the Jenkins server to run the build script to run Unity tests and build the Unity project every time after it pulls content from your Github repository. We can use the bash script we created earlier to run the test and build the project.
However, there’s something more we need to add in between. We want the Jenkins server to detect whether all tests pass, and stop the building process if there are failures. This can be achieved by appending a few lines of bash script after the scripts for running tests:
result=$?
if [ $result -ne 0 ]; then
echo "Unity editor mode tests failed."
exit $result
fi
The above Bash script will get the last return value from the running-test-script, and check whether it equals to 0. If the return value from Unity Test Runner does not equal to 0, that means there are failures, and whole script will stop and exit.
Below is a sample of complete build script:
#!/bin/sh
# Default Unity installation path. Change this if needed.
unity='/Applications/Unity/Unity.app/Contents/MacOS/Unity'
echo "Start running Unity edit mode tests..."
$unity -batchmode \
-projectPath "$(pwd)/src" \
-runTests \
-logFile "$(pwd)/output/editmode_log.xml" \
-testResults "$(pwd)/output/editmode_results.xml" \
-testPlatform "editmode"
result=$?
if [ $result -ne 0 ]; then
echo "Unity editor mode tests failed."
exit $result
fi
echo "Start running Unity play mode tests..."
$unity -batchmode \
-projectPath "$(pwd)/src" \
-runTests \
-logFile "$(pwd)/output/playmode_log.xml" \
-testResults "$(pwd)/output/playmode_results.xml" \
-testPlatform "playmode"
result=$?
if [ $result -ne 0 ]; then
echo "Unity play mode tests failed."
exit $result
fi
echo "Start building the project..."
$unity -batchmode \
-projectPath "$(pwd)/src" \
-logFile "$(pwd)/output/build_log.xml" \
-buildOSXUniversalPlayer "$(pwd)/build/build.app" \
-quit
After we have the build script, we need to tell the Jenkins server to run it. Scroll down to the Build section. Click the Add build step button, and select execute shell. In the text field, type $WORKSPACE/build.sh
. $WORKSPACE
is the absolute path of the directory assigned to the build. Click Save, and you should be all good.
Next step
The above script will store the build result in the $WORKSPACE/build/
folder. This will get override after each build. Usually people will store the build result on somewhere else like a dedicate server, or integrate with other solutions to deliver directly to AppStore, and etc. You have many options here.
That’s it for the overview of Unity test and continuous integration using Jenkins. Hope you get a high level idea of how to set up this kind of system with Unity.