Unity Integration Test 01

Print

We are going to analyse our first Given-When-Then scenario. If you are not used to this type of test, please consider reading one of the previous paragraphs to be sure to understand.

 

When you click on the first test, you can see its structure in the inspector panel.

 

 

Like a standard Integration Unity Test, there is the component called “Test Component”. It is the very same component you are used to having in your Unity Integration Tests. You can just change the Timeout value and the “Included platforms”. Leave the other values unchanged.

The second component is called “BDD Extension Runner”. It is the core of the BDD Extension Framework. It allows you to configure the test properly. It runs the scenarios when you execute the test.

The third component is called “Creation Of Game Object BDD”: It is the class that contains the methods that describe the various scenarios. When you are going to develop your own tests for your software you will create a new class where you are going to put your test methods.

How can we describe our test scenario? Using the Given-When-Then form:

This exact scenario is shown under the “BDD Extension Runner” inspector. Each line corresponds to a specific step of our scenario and a specific method inside the class “Creation Of Game Object BDD”. This scenario is going to implement the first requirement: “When I press the button “Create” I expect to see a new object in the scene.

 

 

Let’s analyse in details the inspector for the BDD Extension Runner:

 

How does the class CreationOfGameObjectBDD looks like? We are going to explore it right now. You can open it easily using the “Edit Script” function (if your favourite IDE is already configured):

 

 

Otherwise, you can open it manually locating it under the “example” folder.

 

First of all, we would like to spend some words about the naming conventions.

Using a naming convention for class names and methods names is a good practice. In these examples, we are not using a particular naming convention, just for keeping the names easy to understand without overloading the explanation with other concepts.

public class CreationOfGameObjectBDD : DynamicBDDComponent

{
[...]

Just have a look at the class declaration. It inherits the class DynamicBDDComponent. This particular base class is a MonoBehaviour component, so it let you add the CreationOfGameObjectBDD class as a component of the integration test.

Secondly, it makes the CreationOfGameObjectBDD class a BDD Component, recognisable by the BDD Framework.

Furthermore, it contains the definitions of other features that we are going to explore soon.

You can choose, of course, the name of the class you prefer: just think about to use a particular name convention, so you can easily understand what part of the software the class is going to test.

[Given ("the software is just started and waiting for an input")]
   public IAssertionResult StartedAndWaitingForInput()
   {
[...]

[When ("I press the button \"Create\"")]
   public IAssertionResult PressTheButtonCreate()
   {

[...]

  [Then ("an object named \"object for test\" has to appear on the scene")]
   public IAssertionResult TheNewObjectAppears()
   {

[...]

For each method declaration, we can notice two important elements: The attribute and the return value of the method. It is the standard declaration of a method you can use with the BDD Framework.

As you can see, each method has declared a different attribute, depending on its role in the scenario. The methods with the attribute Given can be used only in the Given step. Opening the corresponding combo box for Given steps on the Runner Component on the inspector, it lists only the methods marked with the Given attribute. Same for the When attribute and the Then attribute.

Inside the attribute declaration, there is the description of the step in the scenario. This text appears on the BDD Runner when the method is selected.

The return value of the method is an interface called IAssertionResult. The method has to return an object implementing this interface for telling the framework the result of the method execution.

 

There are three possible implementations you can use as the return value:

  1. AssertionResultSuccessful(): When the execution of the method ends without any error, you have to return this object. You do not need to give to the framework other information: it knows that it can continue to run the next scenario step. It is the common return value of the Given and When steps and, if the test described by the scenario is passed it is also the return value of the Then step.
  2. AssertionResultFailed(string text): Returning this object, you are telling the framework that something went wrong during the execution of the step. It is conventionally used only in the “Then” steps, to communicate the failure of the test execution (if it is failed, of course). The string text passed to the constructor is the description of the error.
  3. AssertionResultRetry(string text): Sometimes you cannot be sure about the validity of the test execution until the software reaches some conditions. The execution of all the code in Unity not always is made between an “Update” call and the following one, so sometimes you have to perform the test only when you are sure that your software has reached the right state. Inside your methods, you can put the code to verify if your software has reached the right state. If you have to wait some time, you can return the object AssertionResultRetry to tell the framework to try again the execution of the method. After a default timeout of 3 seconds (but shortly we are going to see how to personalise this timeout), the framework stops the iterations on this methods, using the return value as a failure result. The text passed to the constructor is the description of the error that is shown in a case of timeout.

 

Inside the methods, you can just write your code as you are used, dealing with the objects in the scene, checking the components and their public properties and so on. The only thing you have to keep in mind is to return the right  IAssertionResult object, depending on the behaviour you are expecting from the framework.

Inside the class, you can put other methods in addition to the step methods marked with the “Given” ”When” “Then” attributes. These methods are not directly used or even recognised by the BDD Framework, and they are there just for your convenience.

 

Q: I was expecting to use Asserts in the code to have the result of my test. Why not?

We have considered implementing the framework in a more standard way. However, some test in the Unity World cannot be reduced to a single comparison between two simple values. Furthermore, some features in the framework could not be used properly just managing Assert constructs, like, for example, the Retry feature.

 

Q: I can see other “Given” “When” “Then” methods in the class. What are they?

They are used by the other three integration tests. We are going to analyse them soon.

 

Q: Do I have to return always an object? Can the return value be null?

You have to return always one of those three types. If you return null, the framework interprets it as a coding mistake.

 

Q: How do you suggest to code the tests? Are there some guidelines?

Yes, sure, there is the section BDD Extension Framework Guidelines. There you can read how to organise your tests and how to code them more efficiently.

 

Q: Is the code on the CubeManager class written using BDD?

Yes, it is, but there is just a line in the code that is not developed using the BDD guidelines, we are going to analyse it shortly. Consider it like an Easter Egg.

 

Q: Why do I have to return an AssertionResultRetry object? Can I just use a Thread.Sleep or just a cycle inside my step method?

Every step method is executed during a Unity Update or Fixed Update cycle. If you put some code inside your method that waits for something, your software is going to be frozen inside that method, stopping your software to run.

 

Q: Do I always have to compose the test choosing the methods in the inspector? Can I write a single class for each test and write inside it what I want to run?

No, you do not have to. There is another way to code your tests that do not imply the use of the inspector. You can read the chapter Static BDD Test: Creation of a GameObject for learning about it. However, we suggest finishing to read this chapter, so you can learn the basic information you need.



Back to: Dynamic BDD Test: Creation of a GameObject Read next: Unity Integration Test 02