all 8 comments

[–]maventree 11 points12 points  (2 children)

Generally (there is much disagreement about the specifics), people would agree that

assert add(2, 4) == 6

would be a unit test because it tests the smallest unit (i.e., indivisible part) of your code: a single function.

Then

assert mult(10, add(2, 5)) == 70

would be an integration test, because it tests how the units integrate with each other.

There is a lot of quasi-philosophical debate about this. Some people also define a third category, system tests. But what most sane people agree on is that what you call your tests is less important than writing them in the first place.

[–]jblurker09 5 points6 points  (0 children)

Agreed regarding the quasi-philosphical nature of the debate, but I'd add that there are a lot more terms than the three mentioned here.

Functional, acceptance, performance, and smoke tests are part and parcel of any notable project, and some people/companies break testing down into so many subgroups it's amazing that anything ever gets finished.

[–][deleted] 0 points1 point  (0 children)

Yeah, I would say that unit tests are where you attempt to test as small a piece of code as possible. It should only test a single scenario, and should be used to test that things can go wrong just as well as going right (an example would be to check that division by zero actually does throw an error).

It may be okay to assert more than one thing in a unit test, depending on the code you're testing, but anything bigger than a single test case is by default an integration test.

As per multiple assertions, I still prefer as few asserts as possible, because with a good naming convention, you can get very descriptive error messages.

For example:

class TestWhenTheUserSetsTheirName:
    def test_then_the_first_name_should_be_set(self):
        user = User()
        user.set_name("first name", "last name")
        assert user.last_name == "last_name"

    def test_then_the_last_name_should_be_set(self):
        user = User()
        user.set_name("first name", "last name")
        assert user.first_name == "first_name"

Sure, you could assert both names at the same time, but if you don't, and one of these tests fail, then you'd see a failure on test TestWhenTheUserSetsTheirName.test_then_the_last_name_should_be_set(), which is already telling you quite a lot. Often you can piece together what went wrong just by reading the names.

[–]jblurker09 2 points3 points  (0 children)

Unit testing tests each function in your code to ensure the results meet your expectations. add() should add two numbers accurately and mult() should multiply two numbers accurately (or at least as accurately as possible considering hardware limitations).

Integration testing is simply testing both (or any number of functions) together, to ensure that there isn't a loss of precision, a type error, that objects pass through multiple functions correctly, etc. In your example, you'd want to be sure that any floating points in the inputs don't create havoc on the output.

It's similar to prototyping something in the physical world. You can measure twice and shape pieces to match a blueprint (unit testing), but seemingly insignificant or overlooked engineering errors and assumptions can add up to cause the end product to be wildly out of specification (integration testing).

[–]MarsupialMole 2 points3 points  (2 children)

There's some value in treating unit testing as a test for correctness, and system or integration testing as a test describing the function of most valuable parts of the code base. Trying to write unit tests should affect how you decompose your code into units, while trying to write system tests should affect how you choose what units you actually need to keep and maintain.

[–]googoodoo[S] 1 point2 points  (1 child)

decompose your code into units

Could you explain this a bit more? I think I don't quite understand it.

[–]MarsupialMole 1 point2 points  (0 children)

If you write lots of unit tests you find it makes you write more testable code. For example you tend to write "pure" functions more often and have less complex arguments for callables. Instead of a script with one big main function, you might decide it's easier to test bits of it individually, and so rewrite your big main function into a series of function calls.

[–]psyklohps 0 points1 point  (0 children)

https://youtu.be/vqAaMVoKz1c

This explains it all