How to use Unit-Tests

Started by
7 comments, last by ChaosEngine 7 years, 7 months ago

Hello forum!

I'm trying to get my head around unit testing.

I chose this library, Catch: https://github.com/philsquared/Catch

I have a rather large project and I do not really understand on how to add tests to it.

Maybe I'm still misunderstand unit testing.

Should I store unit tests in extra files? When yes, why?

I thought I would do those once I obtain parameter values or execute a void and see if what I expected happened - checking whether code behaves as expected.

But it seems more of writing tests for a specific testing aspect in a different file for all possible cases:


TEST_CASE( "Factorials are computed", "[factorial]" ) {
    REQUIRE( Factorial(1) == 1 );
    REQUIRE( Factorial(2) == 2 );
    REQUIRE( Factorial(3) == 6 );
    REQUIRE( Factorial(10) == 3628800 );
}

Code excerpt from: https://github.com/philsquared/Catch/blob/master/docs/tutorial.md

Therefore, testing mathematical calculations is probably not a thing that should be tested for every possible outcome.

But testing whether my buffer-instance owns one instance after pushing one instance, would be a unit test?

Though, would I invoke them into the actual code or are they just residing in another file? And if yes, how do they actually interact then?

An example:

My "real" code that tries to solve a problem, runs a for-loop as often as needed (not known at compile time) and push one object per iteration onto the buffer class instance.

Now, it will be run seven times. How would a unit test look?

When it is in another file, it would be run first and only check whether adding one unit will add one unit to the buffer (assuming it owned 0 from its construction point). Another test would be checking on if the size of buffer is increasing after it reached it maximum capacity?

So that would be 2 unit tests.

I always thought I would just simply add REQUIRE( buffer_object.get_size() == i ); inside the for-loop.

I think, it would help a lot if you could show me a simple example on how to invoke unit testing into a tiny code example using a class and whatnot.

The greatest issue of understanding seems to be, where to place the unit tests? How to invoke them into code?

Advertisement

When you write:


#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"

Then this will declare a main function for your program that runs all of the "TEST_CASE" code -- therefore you should write your test cases in a completely seperate executable project than your game.

Unit tests are separate to the game and ensure that each small component works as expected. A test case should use the component in multiple ways, as many as feasible while testing every big of functionality. These unit tests should be runnable without running the actual game, to check if each small part of the code works as expected.

For testing that the state of your program is valid within the normal execution of your game, you should use an assertion library, not a unit testing one (you can/should do both though). Assertions test that everything is valid as part of normal code execution and help you catch crash bugs in the game itself -- before something goes wrong enough to cause a crash, it will/should cause an assertion failure first, which will allow you to break in a debugger and gather information on what went wrong.

Unit tests first and foremost are a way to ensure permanence. They are there to ensure that when you provide an input, you get the expected output. Normally it also checks correctness, but sometimes the tests don't test the correct thing. Whatever they test for becomes permanent.

Therefore, testing mathematical calculations is probably not a thing that should be tested for every possible outcome.

Correct. You want to test enough to ensure the function is doing the right thing, but you don't need to test every value. Often that means testing both sides of boundary conditions, and perhaps an easy to verify value in the middle.

Generally in test writing you have a test that hits the happy path -- doing everything right -- then a bunch of tests that excercse all the error conditions.

Also another key about unit tests is that they need to be fast. If they hit a database, hit a file system, hit a network, they are not a unit test they are an integration test.

I think, it would help a lot if you could show me a simple example on how to invoke unit testing into a tiny code example using a class and whatnot.

It is Java rather than C++, but I've seen this tutorial used several times for starting to build Tetris using test driven development.

You will need to adjust it to your preferred testing framework, but that is generally easy to do.

There's also this amazing book that is freely available as a web site, less of a tutorial and more of a reference, it works through common patterns and struggles in automated tests.

In case it wasn't clear above, your unit tests form part of a completely separate program. They reference the same classes, functions, and structures that your main program does, and perform tests to check that those objects behave as you expect. But they don't exist inside your main program, at all. Instead, you run the test program separately, to ensure that any recent code changes haven't broken any tests. You might even run it as a pre-build step if you have that capability, so that you don't waste time building your project if you've altered code in such a way that the tests now fail.

Thanks a lot!

Telling me that it is an actual different program is already informing me a lot.

I'm really thankful for all of your comprehensive descriptions : )!!

One more question, I would have to create a new project in my solution and rebuild all my classes into one (or multiple) files?

You don't have to touch your original files. You probably do want a separate project that contains the unit tests, and which can build an executable for those tests.

I don't know what you mean by "rebuild all my classes" but whatever you think you need to do here, you probably don't.

Well, in order to test my classes, I would have to implement or rather copy them from the original project.

Is that right or is there any possibility to refer to my original files which saves rewriting/copying a class?

As the projects are separated, I doubt this is possible though.

Multiple projects can refer to the same files - it's not like a directory where a file can only be in one at once.

How to do this depends on how you're building your projects. (Visual C++? Or...?)

Well, in order to test my classes, I would have to implement or rather copy them from the original project.
Is that right or is there any possibility to refer to my original files which saves rewriting/copying a class?
As the projects are separated, I doubt this is possible though.

Typically you'd put the thing you want to test into a library project (static lib, dll, whatever).

You would then have an executable project that contains the tests and runs them (depending on the testing framework; some frameworks load dlls into a "test runner")

Finally you have your game executable which just loads your games library and starts it.

So in visual studio, something like
SuperAwesomeGame.sln
+ SuperAwesomeGameStuff (library)
- all your game code goes here
+ SuperAwesomeGame (exe)
- just basically has a "main" that starts your game
+ SuperAwesomeTests (exe)
- contains all the tests and runs them
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

This topic is closed to new replies.

Advertisement