Search Unity

Unit Testing / TDD An Object That Moves Around A Pivot

Discussion in 'Scripting' started by turbosheep, Feb 8, 2016.

  1. turbosheep

    turbosheep

    Joined:
    Jul 19, 2013
    Posts:
    25
    Hi, I'm very new to unit testing / test driven development and just started a test project in which a few cubes rotate around a pivot in space, and the player can shoot the cubes for points.

    I already got stuck. I made my test project, wrote a test function... but how would I assert that the cube is moving around a pivot in 3D space? Is this better done with an integration test? And even then, how would you know the cube is rotating around a pivot without tons of checks / collision triggers validating the path of the moving object?

    If there are any experienced unit testing programmers out here, help would really be appreciated. :)
     
  2. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    if an object is rotating around a pivot the distance between the object and the pivot should remain constant.

    Seems like an odd thing to unit test though o_O
     
  3. turbosheep

    turbosheep

    Joined:
    Jul 19, 2013
    Posts:
    25
    Thanks for the reply. That's a good point, that the distance should remain constant.
    I'm unit testing it because TDD dictates that tests should be written before production code.
    Whether or not this is feasible, I have no idea yet. I'm learning this concept from books like the one from Kent Beck, which aren't focused on game development.
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    TDD in the context of Unity is hard.

    Say you're writing a car game, and you want to introduce a feature where a car gets damaged when it crashes into a wall. You'll want to set up a unit test for this. So, the Unit test would create a car, call the collision method, and assert that the car's front is damaged. Easy, right?

    Well, first of all creating objects is a bit tricky - you don't have constructors, so you'll have to instantiate game objects and add the correct components. But it's doable.

    The collision is something completely different. You can't simulate a collision between two game objects, so you'll have to call the OnCollisionEnter method yourself. This already creates a problem, in that it forces you to make OnCollisionEnter public. But, once you accept that, you should be good, right?

    Nope! While Collision does have a public constructor, all of it's properties are read-only. So you really can't create a unit test for a collision without either setting up a quite cumbersome reflection framework that rips open the internals of a collision, or by creating a mockable layer between the Collision class and your collision handling code.

    That's just one example. There's two solutions I've been wanting to try, but haven't had the opportunity to. They are:

    - Make a testable game in pure code, think of it as your game model. Use Unity as the view for that model. So all your data and events live in your code, nice and testable and oblivious to the existence of Unity. Then you use Unity to draw those things, and perhaps send physics results if that's neccessary.

    - Do TDD as usual, but use Unity's "integration tests" as your Units. Essentially, you're testing things happening in your game, and you do that by making small scenes and asserting the state of that scene after some time has passed. I've made some small experiments with this, and while it's promising, you do get problems where differing framerates causes tests to fail some of the time, which is the worst thing. This also takes a lot more time than a normal set of Unit tests, as Unity has to play back each test. I tried speeding the time scale up, and that caused the framerate problem to become a lot worse.

    I think both are viable, but takes a great deal of set up. My advice to you is to not learn Unity and TDD at the same time. They can be combined, but you should know how both works in order to do them, as TDD was never taken into account when the core design of Unity was done.
     
    ben-rasooli and Nigey like this.
  5. turbosheep

    turbosheep

    Joined:
    Jul 19, 2013
    Posts:
    25
    Wow, thanks for your elaborate response! Really helpful!
    Nice style of writing and explaining as well, I appreciate the effort.

    It sounds to me like the only sensible way to go about this is to constantly shift between a TDD mindset when handling low-level code that is more about arranging/storing data, calculations, etc. and a post-production-code integration test mindset when dealing with actual gameobject actions/interactions. So I'm leaning towards the second option that you outlined. The changing framerate sounds worrying, and I wonder what causes it. Different systems?

    Time-wise, I'd just run all the tests before introducing a new feature and maybe at the start/end of the day. I don't suppose an entire integration test-suite would take more than an hour.
     
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    With regards to the changing framerates, it's simply what you have running in the background. Running a scene if Unity's the only open program versus running it while YouTube is streaming on a different screen will affect your game.

    When I was experimenting, this had subtle influences that made some tests fail. A big factor is that you get more Updates per Fixed Update if you've got more processor time left over for Unity, so things would have slightly different positions.

    The worst was if this caused tests to fail 1 time out of 1000. If you're running 1000 tests, chances are one of them will fail, and your test run will fail. But you'll have to rerun that test very_many_times before the test happens again.

    This was fixable by adjusting the tests so they were framerate-independent , but it's still an issue you don't have to deal with in "normal" TDD. The good thing is that this problem can expose that parts of your code are dependent on framerate, which is generally a bad thing that should be avoided.
     
  7. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Just throwing 2 cents in here. I did reading for a while about TTD and game development. From what I found they come from two very different areas. Firstly this guys quote here:

    I can't find the other quote annoyingly, but the one I read said a game engine will almost, if not completely, be done using test driven development. It's low level code, all it's features are pretty much set in stone, and have a direct input/output, without (too much) crazy unexpected behaviors that come alongside it. It's very easy to test. Game code however, can be almost put in the realm of 'experimental code', where a lot more random occurrences happen between different actions. About 90% of the game is already fully TDD'd: The engine. The code being written in the game itself is more towards smoke, stress, integration, functional, and usability testing. Rather than specifically unit testing. Unit testing IMO is great when you're writing a low level, or predictable/non-dynamic system in Unity (like an RPG's stats or something), or when you're creating a managed/unmanaged dll for Unity, where you can actually just make a Unit Testing project with NUnit, however in Unity that's where the higher level action happens, and you'll want to test how things react to each other. I am not saying though that it isn't good practice to TTD as much as you can lol, but that's my 2 cents.

    Again this is all IMO, so filter through what fits best with you.
     
    Last edited: Feb 8, 2016
  8. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Oh and btw they do a pretty long training session here on how to test objects in the game. This isn't unit testing necessarily, but it's a good idea on how to test on a different level:
     
  9. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I agree, TDD is hard in a game; it's also hard in desktop programming (which has been my day job for years), and for the same reason: much of what you'd want to test is actually code supplied by frameworks, which really aren't designed to be testable. They show things to the user, and respond to user interaction, in ways that are very hard to automatically verify.

    The only solution in both cases is to move as much of your program logic as possible into the "model" layer of your application, and out of the "view" layer. So, taking it back to Unity for example, if you're writing a strategy game where the game state can (and probably should!) be represented in data structures entirely separate from the scene graph, then you're in good shape — you can unit-test the snot out of that data and logic (and your AI programmer will thank you profusely when he needs to manipulate those same structures in memory in order to plan moves or whatever).

    But some Unity games don't have any such abstractable state; their whole gameplay relies on the physics engine banging stuff together, for example. In this case, TDD is pretty hopeless. Of course you'll probably still have some utility code here and there that does text processing or math or something, and you can use TDD on that, but on most of your game — alas, no.
     
  10. turbosheep

    turbosheep

    Joined:
    Jul 19, 2013
    Posts:
    25
    To everyone, thanks for the elaborate and useful replies. From what I gather: TDD is impractical for pretty much every scenario except low-level code dealing directly with data. For everything else, integration/assertion testing is both easier and faster to implement (nicely demonstrated in the video posted by Nigey).

    If anyone has anything to add to this, including resources, videos etc., please feel free to post here or send me a PM.
    Creating a reliable testing suite + TDD for a Unity project is part of my graduation subject and any help/experience would be greatly appreciated. Thank you all!
     
  11. TheD0ctor

    TheD0ctor

    Joined:
    Sep 29, 2016
    Posts:
    13
    You can do TDD for the most part in Unity, however, you will need to decouple code that depends on GameObjects/MonoBehaviours/Time, i.e. all classes that depend on the UnityEngine and unit test the rest independently. However, I find that it is often the case that I really need a GameObject to test things properly. In short I haven't found a satisfying solution yet, nevertheless this (apparently abandoned?) project seems to be heading in the right direction is, or rather was, Uniject.

    A potential, but pricy, alternative seems to be uServer that claims to emulate UnityEngine, but since I haven't purchased it, I can't comment more on its virtues and flaws.