How to make unit tests for a game engine?

Started by
4 comments, last by DerTroll 4 years, 7 months ago

I'm struggling understanding how to make tests for a game engine. Until now, if I had a console app without graphics that was easy. Its function, class, had its own test.

Now with a game engine its more complicated. In order to test something, I have to create an app which initializes everything (glew, glfw, etc) and starts a window. For every test, I have to do this and at the end of the test, the app is getting destroyed.

Question 1. Is it better to create an app that will live for the entire test cycle and basically all tests will use that app reference to test its functionalities?

Question 2. What happens when a test must test graphics? The test slows down in this case a lot, while the meaning of unit tests is to run fast and be non-blocking. Usually in this step tests block (user input for example).

Usually, when I had to test programs like os functionality or anything else that was just a console app, it was quite easy to create unit tests. Now I'm struggling a lot...

 

Any good bits of advice or links to tutorials that can help? Thanks!


void life()
{
  while (!succeed())
    try_again();

  die_happily();
}

 

Advertisement
44 minutes ago, babaliaris said:

Now with a game engine its more complicated. In order to test something, I have to create an app which initializes everything (glew, glfw, etc) and starts a window. For every test, I have to do this and at the end of the test, the app is getting destroyed.

I use a new Render Window for each OpenGL test. If you wrap this basic setup into a management class (maybe as a singleton) and reuse it in every test, it won't bother you much. It's just including a single class which handles context initialization and destruction itself.

 

47 minutes ago, babaliaris said:

Question 2. What happens when a test must test graphics?

 

Not sure what you mean exactly. I would keep it simple. For example, if you want to know if your "RenderObject" function works, create an Object consisting of a single triangle. Disable all fancy filtering functions, use orthogonal projection and render to a low-resolution buffer/texture that you can read back to the CPU. After getting the render result from the GPU you can compare the rendered "image" to the expected result image (with some tolerance of course).

 

1 hour ago, babaliaris said:

The test slows down in this case a lot, while the meaning of unit tests is to run fast and be non-blocking. Usually in this step tests block (user input for example)

You have to design your tests accordingly. A unit test should, as you said yourself, never block. So if input is required by the tested function you have to figure out, how your test program can generate this input itself.

 

Also, keep in mind, that it does not make sense to test everything only for the purpose of testing stuff. Unit tests should help you to avoid mistakes/bugs during the development and not cause you sleepless nights because you can't figure out, how to tests a specific function. Test for results that you expect. That doesn't mean you test for every possible outcome. If you missed something relevant that caused a bug and wasn't covered by your existing tests, add a specific test for it.

 

Greetings

It sounds like you're confusing unit tests with end-to-end or integration tests.

A unit test should test a single unit not an integration so you do not, by definition, initialise a window, glew glfw or any of that for a unit test.

So, if you are finding that your unit tests are looking a lot like integration or e2e tests then the best advice I can give is to break down your code into more-independent units to decouple those dependencies and allow them to be mocked/stubbed/faked as appropriate.

On the other hand if you really are asking about e2e and integration tests then they don't need to be 'fast', sure faster is better, but you can always run them overnight and see the results the next morning.

2 hours ago, dmatter said:

It sounds like you're confusing unit tests with end-to-end or integration tests.

A unit test should test a single unit not an integration so you do not, by definition, initialise a window, glew glfw or any of that for a unit test.

So, if you are finding that your unit tests are looking a lot like integration or e2e tests then the best advice I can give is to break down your code into more-independent units to decouple those dependencies and allow them to be mocked/stubbed/faked as appropriate.

On the other hand if you really are asking about e2e and integration tests then they don't need to be 'fast', sure faster is better, but you can always run them overnight and see the results the next morning.

No, I'm not referring to e2e tests. My mistake, I misled you. Let's forget about user input.

What I want to do is to check if my functionalities are working correctly. For example, when an object is being created, does it have the correct data in each member? When a method is called, does it return the correct answer?

The problem is how exactly to do this.

When I was in college, I had an awesome professor who had created unit tests for an operating system course, and basically every time you extended the os with your own stuff, you could create a test in his unit test framework, and the test would work perfectly. I mentioned this because basically os systems are quite similar to game engines, they both run in a loop and do stuff constantly.

How did he do that? I mean, in my case if I call the main loop, that was it, the test will pause when the main loop will get called. Probably I need to create the tests in such way that each test will actually run inside the main loop of the engine right?

Currently, I'm using googletest. Should I create my own framework and integrate it to run inside in the main loop of the engine?


void life()
{
  while (!succeed())
    try_again();

  die_happily();
}

 

4 hours ago, dmatter said:

A unit test should test a single unit not an integration so you do not, by definition, initialise a window, glew glfw or any of that for a unit test.

To my experience, there is not always a clear separation between unit and integration tests. For example: If you want to write a small wrapper around an OpenGL buffer object, which manages nothing else than creation, destruction and data transfer of the buffer object, you might consider this as a 'unit' you want to test. The problem is, that the whole OpenGL API does not work until you initialize a render context. To do so, you usually need to initialize GLEW and GLFW (which can't create a context without a render window - at least as far as I know).

You might call this already an integration test. For me, it is still a unit test. YMMV

 

2 hours ago, babaliaris said:

How did he do that? I mean, in my case if I call the main loop, that was it, the test will pause when the main loop will get called. Probably I need to create the tests in such way that each test will actually run inside the main loop of the engine right?

I think you should write a separate test for each 'unit' and initialize only the parts of your engine that are really needed. Sure, you have to initialize your engine multiple times but it is certainly much easier to find a bug in a minimal example with maybe a hundred lines of code than in a fully initialized engine with thousands of 'active lines'.

 

As I said before, the purpose of a test is to make your life easier, not to complicate the development process.

Greetings

This topic is closed to new replies.

Advertisement