قالب وردپرس درنا توس
Home / IOS Development / Introduction to device testing

Introduction to device testing



Curious how unit testing works in unity? Not familiar with how device testing works in general? If you answered yes to these questions, this guide is for you. In this tutorial you will learn the following about device testing:

  • What is it
  • The value it gives
  • Advantages and disadvantages
  • How it works in Unity using Test Runner
  • Write and run Device Tests that pass [19659007] What is a unit test?

    Before diving into code, it is important to have a solid understanding of which device testing is. Simply put, a device test is … a unit test. :]

    A device test is (ideally) to test a single "unit" of code. Exactly what constitutes a "unit" varies, but the important thing to keep in mind is that a unit test should test exactly one thing at a time.

    You should design a device test to validate that a small logical code snippet performs just as you expect it to in a particular scenario. This can be difficult to understand before writing any unit tests, so consider this example:

    You have written a method that allows the user to type a name. You wrote the method so that numbers are not allowed in the name, and the name can only be ten characters or less. Your method intercepts each keystroke and adds the character to the field name as shown below:

      public string name = ""
    publicly invalid UpdateNameWithCharacter (char: character)
    {
    // 1
    if (! Char.IsLetter (char)) { return; } // 2 if (name.Length> 10) { return; } // 3 name + = character; }

    This is what goes on here:

    1. If the character is not a letter, this code terminates the function early and does not add the character to the string.
    2. If the length of the name is ten characters or greater, it prevents the user from adding another character.
    3. When these two tests pass, the code adds the character to the end of the name.

    This method is testable because it does a "unit" of work. Unit tests enforce the logic of the method.

    Look at Example Device Test

    How would you write device tests for UpdateNameWithCharacter method?

    Before you begin implementing these device tests, you need to think carefully about what the tests are doing and name them.

    Take a look at the sample test method below. The names should make it clear what is being tested:

    UpdateNameDoesntAllowCharacterAddingToNameIfNameIsTenOrMoreCharactersInLength

    UpdateNameAllowsLettersToBeAddedToName

    UpdateNameDoesntAllowNonLettersToBeAddedToName

    From these test method name, you can see that you test that "device" of the work is performed by UpdateNameWithCharacter does what it should. These test names can work long and very specific, but this is useful.

    Each unit of tests you write is part of a test package. A test package houses all unit tests related to a logical grouping of functionality (such as your combat tests). If a single test in a test package fails, the entire test package fails.

     device testing: test suite

    Starting game

    Open Crashteroids Starter project ] [Dukanfinne Download materials button at the top or bottom of this tutorial) , and open the Game scene in Assets / RW / Scenes . [19659009]   device testing: spill a

    Click Play to get Crashteroids started, and then click Start Play button. Use left and right arrow keys to move the spaceship to the left and right.

    Touch the space bar to burn a laser. If a laser hits an asteroid, the point will go up with one. If an asteroid hits the ship, the ship explodes and it is over (with the opportunity to start again).

     device testing: spill to

    Try playing for a while and make sure

     device testing: play wood

    Getting started with Unity Test Runner [19659008] Now that you know how the game works, it's time to write device tests to make sure everything is working properly. This way if you (or anyone else) decide to update the game, you can rest assured that the update did not break anything that worked before.

    To write tests you must first know about Unity's Test Runner. Test Runner lets you run tests and see if they pass. To open Unity Test Runner, select Window ▸ General ▸ Test Run .

    When the Test runner opens as a new window, you can make life easier by clicking the Test Runner window and dragging next to your scene window.

    Configuring NUnit and test folders

    Test runner is the unit's test function provided by Unity – but it uses NUnit framework. As you become more serious about writing device tests, consider reading the wiki on NUnit to learn more. For now, everything you need to know will be covered here.

    In order to run tests, you first need to create a test folder to keep your test classes.

    In the Project window, select the RW folder. Look at the Test Runner window and make sure PlayMode is selected.

    Click the button that says Create PlayMode Test Assembly Folder . You will see a new folder appear just below the RW folder. The default name The tests are fine, so you can press Type to complete the name.

     create test folder

    You may be curious about what the two different categories inside the Test Runner are.

    The PlayMode tab is for tests that will run while in Play mode (as if you were playing the game in real time). The tests EditMode will go beyond the Play mode, which is good for testing things that fit the inspector's behavior.

    For this tutorial you will focus on PlayMode tests. But please experiment with EditMode testing when you are ready. Make sure the PlayMode tab is selected from now on when working with the Test Runner window .

    What happens in a test suite?

    As you have learned, a unit test is a function that tests the behavior of a small, specific set of code. Since a device test is a method, it must be in a class file in order to run.

    The test runner goes through all of your test class files and runs unit tests in them. A class file containing device tests is called a test package.

    A test package is where you logically split tests. You will share the test code between different logical suites (such as a physics test package and a separate match). For this tutorial, you just need a test package, so it's time you created it. :]

    Configure the test collection and test package

    Select the Tests folder and in the Test runner window click Create the test script in the current folder button. Name the new file "TestSuite" .

     unittest create test suite

    In addition to creating the new C # file, Unity also creates another file called Tests.asmdef . This is a assembly definition file and it is used to point Unity to where the test file dependencies are. This is because your production code is separated from the test code.

    If you go into a situation where Unity cannot find test files or tests, double check to make sure there is a collection definition file containing the test package. The next step is to set this up.

    To ensure that the test code has access to the game classes, you will create a collection of the class code and enter the reference in the test device. Click the Scripts folder to select it. Right-click this folder and select Create ings Mount Definition .

    Name the file "GameAssembly" .

     unite test mount file

    Click on the Tests folder and then click the Tests assembly definition file. In The Inspector click plus the button under Construction Definition References heading.

    You will see a field Missing reference . Click the point next to this field to open the select window. Select the file GameAssembly .

    You should now see the GameAssembly mounting file in the reference section. Click Use the button to save these changes.

    If you did not follow these steps, you would not have been able to refer the game class files inside the device test files. With it out of the way, it's finally time to code. :]

    Write your first device test

    Double-click the TestSuite script to open it in a code editor. Replace all code with the following:

      using UnityEngine;
    using UnityEngine.TestTools;
    using NUnit.Framework;
    using System.Collections;
    
    public class TestSuite
    {
    
    
    }
    

    What tests should you write? The truth is being told, even in this tiny little Crashteroids game, there are quite a few tests you can write to make sure everything works as expected. For this training, you will only worry about a few key areas of hit detection and core mechanics.

    Note : When it's time for you to write device tests on a production-level product, it's really worth taking the time to consider every possible sidebar you have to test for all areas of your code. [19659009] For the first test, it would be a good idea to make sure asteroids are actually moving down. It would be very difficult for asteroids to beat the ship if they move away from it! Add the following method and private variable to the TestSuite script and save:

      private game play;
    
    // 1
    [UnityTest]
    public IEnumerator AsteroidsMoveDown ()
    {
    // 2
    GameObject gameGameObject =
    MonoBehaviour.Instantiate (Resources.Load  ("prefab / game"));
    game = gameGameObject.GetComponent  ();
    // 3
    GameObject asteroid = game.GetSpawner (). SpawnAsteroid ();
    // 4
    float initialYPos = asteroid.transform.position.y;
    // 5
    yield returns new WaitForSeconds (0.1f);
    // 6
    Assert.Less (asteroid.transform.position.y, initialYPos);
    // 7
    Object.Destroy (game.gameObject);
    }
    

    There are only a few lines of code here, but it happens a lot. So take a moment and make sure you understand each part:

    1. This is a attribute . Attributes define special aggregators. It tells the Unity compiler that this is a unit test. This will make it appear in Test Runner when you run your tests.
    2. Creates an instance of the game. Everything is nested during the game, so when you make this, all you need to do is test here. In a production environment you will probably not have everything that lives under a single prefab. So, you have to take care to recreate all the objects needed in the scene.
    3. Here you create an asteroid so you can keep track of whether it is moving. The method SpawnAsteroid returns an instance of a created asteroid. The asteroid component has a Asteroid script under RW / Scripts if you are curious about how the movement works ).
    4. It is necessary to keep track of the original position of the claim where you verify whether the asteroid has moved down.
    5. All Unity Unit tests are coroutines, so you need to add a return. You also add a period of 0.1 seconds to simulate the passage of time that the asteroid will move down. If you do not need to simulate a period, you can return zero.
    6. This is the statement step where you claim that the position of the asteroid is less than the original position (meaning that it is moving down). Understanding claims is an important part of device testing, and NUnit provides various assertions. Passage or troubleshooting is determined by this line.
    7. Your mother can't shout at you to leave a mess when device tests are done, but your other tests may decide to fail because of it. : [It is always important that you clean up (delete or reset) your code after a device test so that when the next test is run, there are no items that can affect the test. Deleting the game object is all you have left to do, since for each test you create a whole new game copy for the next test.

    Passing Tests

    Great job! You have written your first unit test, but how do you know if it works? The test runner of course! In the Test Runner window, expand all the arrows. You should see your test AsteroidsMoveDown in the list with a gray circle:

    The gray circle means that the test has not yet been run. When a test runs and runs, it will display a green arrow. If a test fails, it will display a red X. Run the test by clicking the RunAll button.

    This will create a temporary scene and run the test. When you are done, you should see that the test has passed.

    You have succeeded in making your first passed unit test, which claims that raised asteroids move down.

    Note : Before you write device tests, you need to understand the implementation you are testing. If you are curious about how the logic you are trying to work, see the code under RW / Scripts .

    Using Integration Tests

    Before going further down the device, test rabbit holes, now it's a good time to explain which integration tests are and how they are different from unit testing.

    Integration tests are tests that confirm how "modules" of code work together. Module is another vague term, but the important distinction is that integration tests are designed to test how your software works in actual production (ie when a user actually plays the game).

    Say you made a fighting game where the player kills monsters. You may want to do an integration test to ensure that when a player kills 100 units, an achievement is locked.

    This test will include several modules of your code. It will likely involve the physics engine (for hit detection), unit managers (which track unit health and process damage, and transfer other related events), and the event tracker that keeps track of all events fired (such as "Monster Killed"). It will then call the performance manager when it is time to unlock an achievement.

    An integration test would simulate the player who killed 100 monsters and ensured that the performance was unlocked. This is very different from a unit test because it tests that large components of the code work together.

    You will not investigate integration tests in this tutorial, but this should remove the difference between what a "unit" of the work is (and why it is unit tested) against a "module" of code (and why it is integration tested).

    Adding samples to the test package

    The next test will test the game over when the ship crashes into an asteroid. With TestSuite open in the code editor, add the following test during the first device test and save:

      [UnityTest]
    public IEnumerator GameOverOccursOnAsteroidCollision ()
    {
    GameObject gameGameObject =
    MonoBehaviour.Instantiate (Resources.Load  ("prefab / game"));
    Game games = gameGameObject.GetComponent  ();
    GameObject asteroid = game.GetSpawner (). SpawnAsteroid ();
    // 1
    asteroid.transform.position = game.GetShip (). transform.position;
    // 2
    yield returns new WaitForSeconds (0.1f);
    
    // 3
    Assert.True (game.isGameOver);
    
    Object.Destroy (game.gameObject);
    }
    

    You've seen most of this code in the last test, but there are some different things here:

    1. You force an asteroid and ship crash by explicitly setting the asteroid to have the same position as the ship. This will force their hitboxes to collide and lead to games over. If you are curious about how the code works, see Ship Game and Asteroid files in the Script folder.
    2. A period of time is required to ensure that the physics engine's collision event burns so that a 0.1 second wait is returned.
    3. This is a statement of truth and it checks that the gameOver flag in the game script is set to real. The match code works with this flag being set to true when the ship is destroyed, so you try to make sure this is set to real after the ship has been destroyed.

    Return to the Test Runner window and you will now see this new test list there.

    This time you will just run this test instead of the whole test pack. Click GameOverOccursOnAsteroidCollision and then click Run the selected button.

    And voila, even a test has passed.

    Setting Up and Demolition Phases

    You may have noticed that there is some repeated code between the two tests in which the game's GameObject is created, and a reference to where the game script is set: [19659012] GameObject gameGameObject = MonoBehavior.Instantiate (Resources.Load ("prefabs / felt"));
    game = gameGameObject.GetComponent ();

    You will also notice it when the game's GameObject is destroyed:

      Object.Destroy (game.gameObject);
    

    This is very common in testing. There are actually two phases in running a unit test. The phase Setup and Tear Down phase.

    Any code within a setup method will run before a unit test (in that package), and some code in the Tear Down method will run after a unit test (in that package).

    It's time to make life easier by moving this setup and tearing down the code for special methods. Open the code editor and add the following code at the top of the file TestSuite directly above the first [UnityTest] attribute:

      [SetUp]
    public invalid setup ()
    {
    GameObject gameGameObject =
    MonoBehaviour.Instantiate (Resources.Load  ("prefab / game"));
    game = gameGameObject.GetComponent  ();
    }
    

    The attribute SetUp indicates that this method is called before each test is run.

    Then add the following method and save:

      [TearDown]
    public idle Teardown ()
    {
    Object.Destroy (game.gameObject);
    }
    

    The attribute TearDown indicates that this method is called after each test is run.

    With the creation and demolition code compiled, remove the lines of code shown in these methods and replace them with corresponding method calls. Your code will look like this afterwards:

      public class TestSuite
    {
    private games;
    
    [SetUp]
    public invalid setup ()
    {
    GameObject gameGameObject =
    MonoBehaviour.Instantiate (Resources.Load  ("prefab / game"));
    game = gameGameObject.GetComponent  ();
    }
    
    [TearDown]
    public idle Teardown ()
    {
    Object.Destroy (game.gameObject);
    }
    
    [UnityTest]
    public IEnumerator AsteroidsMoveDown ()
    {
    GameObject asteroid = game.GetSpawner (). SpawnAsteroid ();
    float initialYPos = asteroid.transform.position.y;
    yield returns new WaitForSeconds (0.1f);
    
    Assert.Less (asteroid.transform.position.y, initialYPos);
    }
    
    [UnityTest]
    public IEnumerator GameOverOccursOnAsteroidCollision ()
    {
    GameObject asteroid = game.GetSpawner (). SpawnAsteroid ();
    asteroid.transform.position = game.GetShip (). transform.position;
    yield returns new WaitForSeconds (0.1f);
    
    Assert.True (game.isGameOver);
    }
    }
    

    Testing of spillover and laser fire

    With the setup and demolition of methods ready to make life easier, it is the perfect time to add more tests using them. The next test should verify that when the player clicks New Game gameOver bool game is not true. Add the following test at the bottom of the file and save:

      [UnityTest]
    public IEnumerator NewGameRestartsGame ()
    {
    // 1
    game.isGameOver = true;
    game.NewGame ();
    // 2
    Assert.False (game.isGameOver);
    return zero;
    }
    

    This should start to look familiar, but here are some things to note:

    1. This part of the code sets up this test to get gameOver bool set to true. When the NewGame method is called, it should return this flag to false .
    2. Here, you claim that isGameOver bool is false which should be the case after a new game is called.

    Return to Test Runner and you should see that the new test NewGameRestartsGame is there. Run the test that you have done before and see that it passes:

    Claiming laser motion

    The next test you add will test that the laser ship fires up (similar to the first device test you wrote). Open the file TestSuite in the editor. Add the following method and then save:

      [UnityTest]
    public IEnumerator LaserMovesUp ()
    {
    // 1
    GameObject laser = game.GetShip (). SpawnLaser ();
    // 2
    flow initialYPos = laser.transform.position.y;
    yield returns new WaitForSeconds (0.1f);
    // 3
    Assert.Greater (laser.transform.position.y, initialYPos);
    }
    

    Here's what this code does:

    1. This gets a reference to a created laser from the ship.
    2. The original position is rendered so you can check that it is moving up.
    3. This claim is just like the one in the AsteroidsMoveDown device test, only now claim that the value is greater (indicating that the laser is moving up).

    Save and return to the Test runner. Run the LaserMovesUp test and see that it passes:

    Now you'll really get hold of things so it's time to add the last two tests and complete this tutorial. :]

    Securing Lasers Destroy Asteroids

    Next, make sure a laser will destroy an asteroid if it hits it. Open the editor and add the following test at the bottom of TestSuite and save:

      [UnityTest]
    public IEnumerator LaserDestroysAsteroid ()
    {
    // 1
    GameObject asteroid = game.GetSpawner (). SpawnAsteroid ();
    asteroid.transform.position = Vector3.zero;
    GameObject laser = game.GetShip (). SpawnLaser ();
    laser.transform.position = Vector3.zero;
    yield returns new WaitForSeconds (0.1f);
    // 2
    UnityEngine.Assertions.Assert.IsNull (asteroid);
    }
    

    How this works:

    1. You create an asteroid and a laser and make sure they have the same position to trigger a collision.
    2. A special test with an important difference. Notice how you explicitly use UnityEngine.Assertions for this test? That's because Unity has a special Zero class that is different from a "normal" Zero class. The NUnit framework Assert.IsNull () will not work for Unity zero controls. When you check for zeros in Unity, you must explicitly use UnityEngine.Assertions.Assert, not NUnit Assert.

    Return to the test runner and run this new test. You will see the satisfactory green mark. :]

    To test or not test

    Deciding to commit to unit tests is a great commitment and should not be taken lightly. But the payouts can surely be worth it. There is yet another development method known as Test Driven Development (TDD).

    With TDD, you actually type tests before writing your application logic. You make tests first, make sure they fail, and write only code that is made to pass the test. This can be a completely different approach to coding, but it also ensures that you have typed your code in a testable way.

    Please note this if you decide to take the chance on your next project. For now, it's time to write your own tests, but to do that you need a game – that's all for you.

    Note : Deciding whether to test only public methods or private methods is something you must consider. Some believe that private methods should only be tested through the public methods that use them. This can make the "device" of the code you need to test quite large and may not be desirable. On the front page, testing of private methods can be problematic and requires special frames or by means of reflection tools to check things. Each scenario has its advantages and disadvantages, which are beyond the scope of this training. This tutorial will put all methods to be tested in public to make it easier to follow – so don't use this tutorial as a reference for best practice in terms of production code.

    Testing can be a huge commitment, so it would be valuable to look at the benefits and disadvantages of adding device testing to your project:

    Unit Testing Pros

    There are many key drawbacks to device testing that include the following :

    • Trusts that a Method behaves as expected.
    • Works as documentation for new people who learn codebase (device tests provide good teaching).
    • Forcing you to write code in a testable way.
    • Helps you isolate errors faster and fix them faster.
    • Prevents future updates from adding new errors to old work codes (known as regression errors).

    Device Testing Disruptions

    However, you may not have time or budget to take on device testing. These are the disadvantages you should consider:

    • Writing tests may take longer than writing the code itself.
    • Poor or inaccurate tests create false trust.
    • Requires more knowledge to implement correctly.
    • Important parts of the code base are not easily testable.
    • Some frames do not make it easy to use private method testing, which can make device testing more difficult.
    • If the tests are too fragile (fails for easy reasons), maintenance can take a lot of time.
    • Device tests do not get integration errors.
    • The user interface is difficult to test.
    • Inexperienced developers can waste time testing wrong things.
    • Sometimes it can be very difficult to test things with external or runtime addiction.

    Testing that destroys asteroids increases the point

    Time to write the last test. With the code editor open, add the following to the bottom of the file TestSuite and save:

      [UnityTest]
    public IEnumerator DestroyedAsteroidRaisesScore ()
    {
    // 1
    GameObject asteroid = game.GetSpawner (). SpawnAsteroid ();
    asteroid.transform.position = Vector3.zero;
    GameObject laser = game.GetShip (). SpawnLaser ();
    laser.transform.position = Vector3.zero;
    yield returns new WaitForSeconds (0.1f);
    // 2
    Assert.AreEqual (game.score, 1);
    }
    

    This is an important test that ensures that when the player destroys an asteroid as the score goes up. To break it down:

    1. You are spying on an asteroid and a laser and making sure they are in the same position. This ensures that a collision occurs, which will trigger a point increase.
    2. This claims that game.score int is now 1 (instead of 0 as it starts on).

    Save your code and go back to Test Runner to run this latest test and see that it goes:

    Amazing work! All tests go by.

    Where to go from here?

    You covered a lot of ground here. To compare the work of the final project, find Download Materials at the top or bottom of this tutorial.

    In this tutorial you have learned what unit tests are and how to write them in Unity. You also wrote six device tests that all passed successfully and learned some of the benefits and disadvantages of device testing.

    Feeling safe? There are many more tests you can write. Try to look at all game class files and write device tests for other parts of the code. Consider adding tests to the following scenarios:

    • Each asteroid type triggers the game when it crashes into the ship.
    • Starting a new game sets the score to 0.
    • Moving left and right works properly for the ship.

    If you are interested in taking the device test to the next level, look at Emergency Injection and Mocking Frames. This can make it much easier to configure your tests.

    Also, read through the NUnit documentation to learn more about the NUnit framework.

    And don't be shy, share your thoughts and ask your questions in our forums.

    Happy Testing!


Source link