Clone wiki

UnityTestTools / AssertionComponent

How to use the Assertion Component

Assertion Component overview

The Assertion Component brings you the possibility to assert desired states of your game objects. It's a visual tool that doesn't require writing any code. It was designed to be extensible and adaptable to the content of your project and your needs. 

The way the component works is simple. You need to set up an invariant - a condition you expect to be always true. You specify when the condition should be checked (for example every Update method). Now, when your project is running and an assertion you set fails, an exception is throw. This way you get notified that your application got into an undesired state and you can investigate the issue. In most cases you would want to have the "Error pause" option enabled in the Console window to pause the run when an error occurs.

Image

  1. Comparer selector - A comparer defines how two values should be compared with each other. It determines the result of the assertion.
  2. Frequency of checks - Multiselectable control where you can define when the assertion should be checked.
  3. Custom menu for After period of time frequency option (see 2.) It won't be visible unless the option is selected

    1. After how many seconds should the first check be done
    2. Should the checks be repeated
    3. How often to repeat the checks
  4. Custom menu for Update frequency option (see 2.) It won't be visible unless the option is selected.

    1. After how many frames first check should be done
    2. Should the checks be repeated
    3. How often to repeat the checks
  5. First GameObject that is used in the compare method. By default it's the GameObject to which the component is attached

    1. GameObject reference field
    2. Path to the variable to be checked
  6. Custom field from the selected comparer (Float Comparer). In this case they define operation type and precision.

  7. What to compare the object (selected in 5.) with. It's possible to compare it with another GameObject, static value or null value.
  8. The other object to compare with.

Setting up the Assertion Component

The Assertion Component is really easy to set up. A simple assertion can be set up in just a few steps:

  1. Choose the Comparer (1) that will be used when checking the assertions. A Comparer usually defines acceptable types which will be a helpful filter when selecting property to compare
  2. Select when you would like the assertion to be checked (2). Most of the callback methods of MonoBehaviour are available (like. OnStart, OnUpdate). You can also set the time after you would like the check to be done (After period of time). OnUpdate and AfterPeriodOfTime allow you to select an extra parameter defining the frequency of checks (3, 4).
  3. Choose path to the property (5b) which value you would like to compare. The values will be filtered out based on types accepted by selected Comparer. For example the Float Comparer accepts only float values, so only properties and fields of float type will be presented.
  4. A Comparer can expose fields which can be used to customize behaviour. For example the Float Comparer allow you to select the type of compare operation (Equal, Greater, Less) and the precision of floating point operations (6).
  5. Next, you can select what you would like to compare the value with. By default, you can compare it with another GameObject's property. You can also compare it with a static value (if it's supported by the Comparer) or to null.
  6. Depending on your previous choice, select the other value to compare with.

Create assertion from code

It's possible to create the assertion from script. To do so, use one of the following APIs from the UnityTest.AssertionComponent class:

  • public static T Create<T>; (CheckMethod checkOnMethods, GameObject gameObject, string propertyPath)
  • public static T Create<T> (CheckMethod checkOnMethods, GameObject gameObject, string propertyPath, GameObject gameObject2, string propertyPath2 )
  • public static T Create<T> (CheckMethod checkOnMethods, GameObject gameObject, string propertyPath, object constValue)

The Create static methods take as a generic argument the class of the comparer you want to use. The checkOnMethods parameter flag allows you to set the methods where you want the assertion to be checked. The gameObject is the instance of GameObject from with a property passed in propertyPath parameter will be used is the evaluation.

The gameObject2 and propertyPath2 represent to the path to a property from the second GameObject you want to use in the assertion. Pass the constValue to use is in comparison. 

In addition, each of the method has an overload with an out parameter that return reference to the AssertionComponent that is created. Use it to configure the validation frequency if necessary. 

Examples of usage:

public class InitAssertions : MonoBehaviour
{
    public float FloatField = 3;    
    public GameObject goReference;

    public void Awake ()
    {
        //An assertion that will compare a float value from a custom component attached to a GameObject to a constant variable equal to 3. 
        //The comparison will happen Start method and every 5 frames in the Update method
        //Additionally, the comparer is configured to have accuracy of 0.1 for floating equality check.AssertionComponent ac;
        var c = AssertionComponent.Create<FloatComparer> (out ac, CheckMethod.Update | CheckMethod.Start, gameObject, "InitAssertions.FloatField", 3f);
        ac.repeatEveryFrame = 5;
        c.floatingPointError = 0.1;
        c.compareTypes = FloatComparer.CompareTypes.Equal;

        //Create an assertion that will fail is the FloatField from InitAssertions component of gameObject will change it's value
        AssertionComponent.Create<ValueDoesNotChange> (CheckMethod.Update | CheckMethod.Start, gameObject, "InitAssertions.FloatField");

        //Validate the gameObject.transform.y is always equal to 3 (defined in this component)
        transform.position = new Vector3(0, 3, 0);
        AssertionComponent.Create<FloatComparer> (out ac, CheckMethod.Update, gameObject, "InitAssertions.FloatField", gameObject, "transform.position.y");

        //Check with the goReference field from this component is set to nullvar gc = AssertionComponent.Create<GeneralComparer> (CheckMethod.Update, gameObject, "InitAssertions.goReference", null);
        gc.compareType = GeneralComparer.CompareType.AEqualsB;
    }
}

Assertion Component features:

  • Comparers Comparers define the assertion action. A Comparer must derive from ObjectComparerBase class and implement the Compare method.  Example of a Comparer implementation:  

Image

  1. A Comparer needs to inherit from ObjectComparerBase class. 
  2. Any public serializable fields will be exposed in the Comparer. They can be used for customizing the Comparer. 
  3. The Compare method will be called when the assertion is performed. It's an abstract method that takes as arguments two values of types as defined by the Comparer. If types are not defined (the Comparer derives from non-generic ObjectComparerBase), the argument will be of System.Object type. 
  4. Additional customization can be done by overriding methods. The GetDepthOfSearch method overrides default depth of property search algorithm.  

*-Selecting path to the property of a GameObject When you select path to the desired property, the control will show you a list of fields which types are the same as Comparer's accepted type. The list will contain properties of GameObject itself and properties of all Components attached to it, including custom scripts.

Image

  1. Manual path selection If the Comparer doesn't have accepted type specified, it is impossible to present possible value to pick from. You will need to type in the path to the property by hand. The editor will give you a tip if such path might not be correct and will allow you to pick some values form a hint list. Hint: if you press down arrow while typing the path, the path hints popup will be displayed.

Image

  1. Comparing to constant value You can compare the first value with a static value you will provide in the component. To do that, in the Compare to type field select Compare To Constant Value. If the type accepted by the Comparer is serializable by default in Unity, an appropriate control will appear to put in the values.

Image

How to start

This steps will guide you through the process of setting up a simple Assertion Component

  1. Create a new object on the scene, for example a Sphere, and add a Rigidbody component to it. Select the object and go the Inspector
  2. Add the Assertion Component
  3. Select the Float Comparer
  4. Select the OnUpdate as the moment to verify the assertion. Remember to uncheck the default OnStart selection since it's a multi select control.
  5. Set the Frequency of repetitions to 10 as we don't need to verify the assertion every frame. 
    Image
  6. Select Sphere.Transform.position.y value.
    Image
  7. Select Compare type to Greater as we want the Sphere.Transform.position.y value to be always greater than the value we will compare it with.
    Image
  8. Add another Game Object, for example a Cube, and place it somewhere below the first object.
  9. Drag the cube's Game Object to the Compare to field in the Assertion Component.
  10. Selected path Cube.Transform.position.y
    Image
  11. Run the scene
  12. You will see the sphere falling down and once it falls below the cube, the editor should pause. It happened because the assertion check has failed (the condition Sphere.Transform.position.y > Cube.Transform.position.y was no longer valid). If the editor doesn't pause, make sure you selected Error Pause option in the Console.

Assertion Explorer

The Assertion Explorer is available from the Unit Test Tools menu. It shows all assertion placed on objects of the current scene. Most fields are read-only. You can only disable and enable single components from the explorer. It allows grouping the list and basic filtering.

Image

Code stripping

The assertions will be stripped out from GameObjects in non developers builds. To completely remove any code dependencies a custom build pipeline needs to be incorporated. An example of code stripping build pipeline:

using UnityEditor;
using UnityEngine;

public class StripCodeAndBuild
{
    private static string assetsPath = "Assets/UnityTestTools";
    private static string buildTempPath = "Assets/Editor/UnityTestTools";

    [MenuItem ("Unity Test Tools/StripCodeAndBuild")]
    public static void StripCodeAndBuildStandalone ()
    {
        AssetDatabase.CreateFolder ("Assets", "Editor");
        var result = AssetDatabase.MoveAsset (assetsPath, buildTempPath);

        if (string.IsNullOrEmpty (result))
        {
            BuildPipeline.BuildPlayer (new[] {EditorApplication.currentScene}, @"C:\temp\release.exe", BuildTarget.StandaloneWindows, BuildOptions.None);
            result = AssetDatabase.MoveAsset (buildTempPath, assetsPath);
            if (string.IsNullOrEmpty (result))
                AssetDatabase.Refresh ();
            elseDebug.LogWarning (result);
        }
        else
        {
            Debug.LogWarning (result);
        }
    }
}

Updated