14 Eylül 2016 Çarşamba

Testing in Unity3D

I am not talking about QA testing or user acceptance testing. I am talking about the tests that we as software engineers are responsible while development is on going. I am talking about automated tests. There are different kinds of automated tests like Unit Tests, Integration Tests, Functional Tests etc...

I will not discuss these concepts, their scope or why they are needed. Instead I will tell you what we are doing in our Unity games.

Unit Tests

Unity does not have build-in support for unit testing. But there is a package in Unity Asset Store called Unity Test Tools created by Unity itself. Actually it is an open source project that you can also contribute. If you integrate that tools into your project, you would have the ability to write unit tests in your project. These tools are based on NUnit library, a well-known unit testing library for .Net languages. More information about NUnit can be found on http://www.nunit.org/.

Your unit tests should be placed under Editor folder (it is a special folder in Unity projects). The runner will scan the assemblies (including external assemblies included in the project that reference nunit.framework library) and generate a list with tests that were located. The tests are executed in the editor, in non-play mode, all in one frame. It is not possible to skip a frame and/or execute an API that requires skipping frames.

The code you execute via the test runner will directly affect your currently opened scene. For example, if you instantiate a GameObject, it will be persisted on the scene. Most of the times such behaviour is undesired. Unity prevents to have unit tests on attached scripts (MonoBehaviour scripts). That is why having a Dependency Injection (DI) or Inversion of Control (IoC) framework makes our code decoupled and testable. Having clear separation on game logic and game scene (views) is helping us a lot to have enough test coverage. Our unit testing strategy is try to cover all game logic, and complex logic calculations, not the views and screens.

Integration Tests

Unit Tests are there to check your individual units are working correctly. But you still have to test if these individually tested units can work together. Unfortunately you can not just test your custom units together, you have to involve internal UnityEngine functions. So we are using Integration Test Framework inside the Unity Test Tools.

The Integration Tests are designed to run on a separate scene. You can consider the scene where you place your tests as a test suite. One test scene can contain multiple tests. Unity suggests not to put your tests on your production scenes. Instead, create a separate scene for them.

There are two ways of creating integration tests. Manually by creating a test scene and adding some assertion script on this scene. Or dynamically (from code) by marking a MonoBehaviour with an attribute that make it loaded by the test runner. For more information please refer to official documentation.

We are using dynamic approach to be able to create our integration tests programatically. Because we don't want to create a separate scene for testing purposes. It is very important to be able to test actual production scene with actual user behaviours. We just want to simulate a user playing the game and make assertions while the real play is ongoing in the real game scene.

Although this framework is not designed to use actual game scene, we could manage to make it happen. And we could manage to simulate user actions like button touches or card throws etc...  It opens us whole new possibilities. And we gradually improve our integration test suite, here it is the evolution steps:

  • First we started to write integration test for critical user scenarios like login process, or playing a regular game play. In the beginning it was not easy to create one integration test. 
  • So to be able to make it easier we started to create the integration tests based on log files. What I mean is, we just play the scenario in the editor and record every action and every package exchange between client and server in a log file. And give that log file to our integration tests to parse and simulate all the recorded actions and server messages. And we added other important scenarios like reconnect, or some edge cases on the game play.
  • Afterwards we wanted to create integration tests from real users' play records. We just improved our log system and adapt integration tests to parse the real user actions from log files. To be able to get the log files we attached them to user feedbacks and also crash reports. So that we could re-produce the real scenarios of real players. And add some of them to our integration test suite.
  • Instead of running this integration tests in the editor we started to use batch mode (unity command line tools) to be able integrate it to our continuous integration (CI) system (don't worry I will have one separate post about CI). And schedule it to run twice a day to inform us everything is good and working as expected with all changes.

  • Since these integration tests are real play scenarios, to run all of them took around 20 minutes on average. It might be fine for CI, because it is running automatically and informing us when it is finished. But it is not fine, if you want to run these tests in your local environment. It is not acceptable to wait 20 minutes while you are coding. So we tried to find a way. And end up using Time Manager feature of Unity. We just make the time scale 10 times faster, while running the integration tests, and decrease the total amount of time to 2 minutes. Which is fairly acceptable time for entire integration test suite.

Editor Tests

There is another test a developer should do after (or during) every development. Actually this is not an automated test. Yes, I am talking about manual testing. You should not just rely on automated tests especially during the development. You should absolutely, without any doubt, test what you are coding. I think we all agree on that.

Manual testing is testing what you are doing like you are the user of that feature. And sometimes you are developing something (implementing a feature or fixing a bug) deep inside the the game. Let's say your are developing the result popup that is presented at the end of a card game. And there isn't one single mode of that result popup. It can change according to game type (solo or regular), game result (win or lost) etc...

According to my experience you have to play the actual game and see the result for every main scenario especially after you are certain that development is finished. But during the development you should not always play the game, you have to find a better way. Because you are an engineer, you have to find a more practical way of testing.

The first thing that came to mind is, just write a temporary test code to create that popup with given result model. And try it over and over again. And change the model and run it again to see how it is behaving with this model. It is not a bad approach. You can see some commented code pieces just for testing purposes (most of the case they are out dated) in almost all software projects.

But with Unity Editor there is a better way. You can create a custom editor specific to your needs. By doing that you can test all cases with a single run, and also you don't need to change the real production code with temporary codes. So you don't need to comment out that test code, which means it will live with the project and always up to date. So it can be usable whenever you needed.

So these are the main testing practices that we are using during Unity game development. If you have any comments or additions do not hesitate to comment on this post. Because now I am moving to debugging in Unity.

1 yorum:

Unknown dedi ki...

Casino de California - Trick Tacticoe
Welcome to Trick 럭스 토토 Tacticoe. This site 가입 머니 주는 사이트 is not 스포츠 라이브 스코어 associated with nor is it endorsed by any professional or collegiate league, bet365 우회 association or 안전놀이터 organization, ‎Game Types · ‎Promotions · ‎About Us