Foreign
Foreign

Reputation: 405

How to perform unit testing of complex objects?

Let's suppose you want to create unit tests for a game.

You have the Player class, which has a local variable of the World class (instantiated by the construtor) and the World object has an open connection to the database.

So player.breakBlock() method would call world.breakBlockAt(x,y,z) and world.breakBlockAt(int x, int y, int z) method would perform changes to the database and return the result.

In cases like this where you have complex "object dependencies", what would be the best way to perform testing?

Currently I'm basically starting the whole game environment, with a test database, spawning a fake Player and using it for the testing.

Upvotes: 5

Views: 2671

Answers (4)

m0skit0
m0skit0

Reputation: 25873

Depends on what type of testing you want to perform:

  • Unit testing: this is aimed at testing individual "units" of code, usually classes. In this case you are only interested in the behavior of the specific unit you're testing, so mock all dependencies to simulate the behavior you want.
  • Integration testing: this is aimed at testing the interaction of several units with each other. Since you're interested in only a few units at a time, you mock any modules not in the specific integration test.
  • Functional testing: this is aimed at testing the functionality or use cases of your application. This involves more abstraction on how the code is structured since you want to test the user interaction with the application and not specific code units. This tests usually involve little to no mocking.

Note that mocking usually involves using a mocking library (e.g. Mockito) to make the mocking easier.

Also note that if you're using dependency injection, the testability of the code is much better since you can inject mocks as well.

More detailed information about test types

Upvotes: 3

Crusha K. Rool
Crusha K. Rool

Reputation: 1502

To test your Player class without a database, you could use a mocking framework like EasyMock or Mockito to create a mock of your World object, pre-record the methods you expect your Player class to call and then verify later that the method was actually called.

You can also let your mock objects intercept the method call and replace it with custom logic. You could for example delegate the call breakBlockAt to a method that modifies a HashMap instead of an actual database. And the returned value could then behave differently depending on what previous calls already did to the HashMap.

Of course you should still have a separate test case for your World class as well, to actually test the logic that works with the database (but without adding the complexity of the Player class on top). This is basically an integration test where you make sure that the JDBC statements or JPA objects result in valid SQL that works when used with the database dialect of your choice. A library like Testcontainers can be used to set up an empty database in a Docker container from within your unit test. The only requirement is that your test environment has Docker running and ready for execution.

Upvotes: 3

Nir Levy
Nir Levy

Reputation: 12953

The idea in unit tests is that on each test, you test only one component of your code.

Meaning, on World's test, I'd mock the database connection, and write tests on world.breakBlockAt(x,y,z) where I mock the DB to do various cases.
The idea here is not to test the DB, but:
- that your code passes the correct input to the db
- that your code handles the various results as expected.
- that your code handles correctly all kind of responses from db (for example, handles exceptions correctly)

Same goes to Player - on this one you should mock world, and check that your code in player passes the correct values to World, and handles ok the response from it

Upvotes: 2

Jb31
Jb31

Reputation: 1391

You could mock e.g. the World object (with frameworks like Mockito) in Player, replacing breakBlockAt with a stub and then verify that breakBlockAt has called.

This of course requires that you are able to "inject" such a faked World into the Player class.

Upvotes: 2

Related Questions