Can somebody explain the difference between unit, component, and system testing with a real example. What I understand is unit testing is about testing individual functions with mock services if the functions use any, and component and system is about testing whole classes and their interaction, but really don't actually get the difference between them. Any detailed explanation with a real example would be great.
Disclaimer: There is no standard / widely accepted precise definitions. The best you can do is to learn about the nuances of different contexts, have your own definitions precisely, but when hearing someone, pay attention to what they think each of these things mean - possibly filling the gaps of some points.
Here go some points I see many people use:
- Unit Test
The book XUnit Patterns, by Gerard Meszaros, says the following about Unit Test:
A test that verifies the behavior of some small part of the overall system. What makes a test a unit test is that the system under test (SUT) is a very small subset of the overall system and may be unrecognizable to someone who is not involved in building the software.
The last part connects to Bob Martin's definition: A test made by programmers for programmers.
These tests are characterized by language that makes sense to the internals of your software: The entities that make your architecture and how they behave.
The C2 Wiki has many other definitions.
unit testing is about testing individual functions with mock services
Not necessarily. Mocking is one way to design unit tests, known as the London school/style. You can read more about it here. To embrace both London and Chicago schools, I would recommend the focus on how are the writers and readers of these tests.
- Component Tests
Meszaros says the following about Component:
A larger part of the overall system that is often separately deployable.... A component can and should be verified using component tests before the overall application is tested using customer tests.
Additionally, about Component Test:
Contrast this with a customer test which is derived almost entirely from the requirements and which should be verifiable by the customer. And with a unit test which verifies a much smaller component. A component test is somewhere in between.
I hear this term very often in frontend frameworks. They usually allow creation of renderable objects they call Components. Imagine for instance the Search Bar here in Stack Exchange: It has behavior, can be rendered, etc, but it is not tightly coupled with any user flow - one can inject test doubles to mock the a backend service and get fake responses related to whatever we want. Component tests for it would render only this Component and exercise its logic (without mentioning customer related user flows).
- System Test
Meszaros calls those Customer Tests:
A test that verifies the behavior of a slice of the visible functionality of the overall system. The system under test (SUT) may be the entire system or a fully-functional top-to-bottom slice (or "module") of the system. A customer test should be independent of the design decisions made while building the SUT. That is, we should require the same set of customer tests regardless of how we choose to build the SUT. (But how the customer tests interact with the SUT may be affected by high-level software architecture decisions.)
In summary, these are the tests that talk the language of the customer and exercise the SUT as the user would (which is regardless of how we choose to design the system internally). Note that it doesn't necessarily mean touching the UI, because the UI is a detail - the business logic is not on the UI (at least not for a long time, because if it is, soon the developers will get stuck and not progress).
Dave Farley explains it in more detail here (he calls it Acceptance Testing).