0

I recently asked a question about design and got suggestion about how to structure my code. I'm still working on design so I only have pseudo code, but this is what I had in mind.

class TableManager()
{
    int init(DBManager manager, String name)
    {
        this.name = name
        this.manager = manager
    }

    int add_thing(Thing thing) 
    {
        try {
            manager.cursor.execute("INSERT INTO %s, (%s)) % (this.name, thing)
            return 1
        } catch {
            return -1; 
        }
    }

Initially I figured that you would unittest this by initializing TableManager in the unittest setup by passing it a DBManager connected to localhost and "TEST_TABLE" as the name argument.

Then you would call add_thing with various table states. For example, the first test would call add_thing with an initially empty table. The unittest would then check the status of the TEST_TABLE to make sure the added thing is in the table.

Is this considered integration testing or unit testing?

Someone mentioned using a MockDatabase to unit test the table manager. I don't see what that would do? You could create a MockDatabase which just returns true when execute is called, but I don't see how that would test the functionality of add_thing without actually having a database to make sure the element was added successfully.

2 Answers 2

3

... I'm still working on design so I only have pseudo code ...

Your code is your primary design document as it's the only one that informs the compiler on how to create your app for you. Never feel you have to justify writing code to help clarify high-level designs.

manager.cursor.execute("INSERT INTO %s, (%s)) % (this.name, thing)

Please read up SQL injection and never create SQL statements this way!

Is this considered integration testing or unit testing?

You are relying on a side effect to your test (changing a database). So such tests are integration tests.

You could create a MockDatabase which just returns true when execute is called, but I don't see how that would test the functionality of add_thing without actually having a database to make sure the element was added successfully.

You are right: this is a downside to unit tests. They are fast and easy to create, but they test code in isolation. It could test that some sort of string is passed to manager.cursor.execute, it could even do some parsing of the string to test it for validity, but to truly test this code you need a real database and an integration test.

0

An unit test tests whether a component works as expected, in isolation. The focus is on the behaviour of the component.

An integration test tests whether multiple components cooperate successfully. The focus is on their interaction.

A good mental framework for unit tests is the concept of the value added test[1]. A unit test does not check the complete behaviour of a component including the behaviour of other components used by it, but tests only the value added by that code. Why do we need that code? What would happen if we deleted it? That should be the subject of a concise unit test.

[1]: see Quality Code. Software Testing Principles, Practices and Patterns by Stephen Vance.

Here, the code in that unit just calls manager.cursor.execute() with certain values and turns exceptions into return values. To test that code as a “value added” unit test, we would inject a mock DBManager instance. This mock then asserts that it is given certain values, and we can specify what it is supposed to return. We can then test:

  • The DBManager cursor is called with a specific string argument.
  • When execute() returns normally, add_thing() returns 1.
  • When execute() throws, add_thing() returns -1.

And that's all an unit tests should do, because that's all the code you are testing does.

If we use a real database that would be an integration test – we are not only testing the class but also the DBManager and the database system itself. The tests would now be:

  • When I add a thing, it appears in the database in the correct table.
  • add_thing() returns 1 on success.
  • Upon some error, add_thing() returns -1 (e.g. when a non-existent table name is used).

In practice, the difference between integration tests and unit tests is not drawn as clearly. Instead of being pure, tests should be useful. If it has more value to cleanly test your TableManager in isolation do that. If it has more value to set up a test database, do that instead. Colloquially, all automated tests of single classes are called “unit tests”, even when the tests rely on external services like databases. Keep in mind though that isolated tests tend to run faster and are more resistant against unrelated changes in other parts of the system.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.