Testing is an essential component of quality during any process. App development is not an exception.
Often, during testing, we are faced with a situation, when real data is not available, or some part of real implementation logic is complex and currently not tested but required to be used, so some-how simulated. Such situations require additional efforts and increase the complexity of tests.
Intro
Complexity - is something that we would like to keep at a minimum in most systems.
There are a lot of different concepts that can be applied to the process of creating a good test - F.I.R.S.T., Given-When-Then, etc. All of them propagate simplicity as one of the core concepts.
if u find it hard to determine simplicity criteria - u can refer to results provided by Kent Beck
Another reason for reducing complexity - is speed. The complex test is slow, but we always need speed in a test - this allows us to run tests every time we want and get an almost instant report about the current product quality state.
Returning to tests, we also can apply simplification to a testing subject in parallel to the tests themselves. This makes any test even more clear and concrete - everyone can easily understand what tested, understand why it fails (if any), and understand how some part of code should work.
How we can simplify testing code? To answer this question we should review the reason for this question.
Many objects use dependencies - so every testing need’s to create them. Imagine a situation when dependencies use another dependency and so on (not a very good coding practice, but it’s life, the ideal situation is rare. Rare because of time cost, rate because of some other reason. We always can find some :], but truth is that because we can make an error because sometimes we are too lazy to do things right).
Another case - we may use remote service in the real-time application or some async result, but how we can test such response or some other operation related to it? How to test some complex precondition for some situations? How to make it fast? How to get the same input for tests every time?
Yep, a lot of cases…
For simplification, we can use something that simulates these complex operations, something that equivalent and not important for the testing system, something that represents data structure but not a real one.
The common name for such objects - Test Doubles.
Test Double - object that can be used instead of real objects during a test.
We may require different objects for different test purposes. Indeed, this object has few types: Mock, Stub, Fake, other.
Fake object - object that has the same functionality as a real object, but different implementation. Often this implementation is simplified.
Fake objects usually used as shortcut for required functionality - this provide speed and expected result. It’s often an object that adopts some requirements from the protocol.
Often, as a sample u found an in-memory database - such a database is never will be used on production, but for the test, the purpose is the perfect one.
One more property of such objects - they are not affect the SUT (system under test), instead, these objects simplify the process of testing, and so, have some limited capabilities.
Stub
Stub object - object that has some dummy data for specific calls required by tests and some mechanism that allow return specific value required by tests - some state.
Every time we need to check if some call is executed, instead of introducing a complex solution using a real object, we can simplify it by using Stub one.
Sometimes u can hear Spy object - this is the same Stub object but with ability to record received information about how they are used.
Such objects are also not used in production and so may behave in a strange, not-real manner. This is ok, thus the purpose of such an object is to simplify the process of testing.
A good example may be REST API response - u always know that some operation during the test will return the expected result - predefined answer.
Mock
Mock objects - objects that used to register received calls from test object and so we can verify it. This type of object is also not used in production. In other words, object contains some state, that can be cheked during test.
Sometimes Mock object is called a special type of Stub object with extra states inside. Additional states and some other parameters allow checking whenever execution is processed expectedly. So verification is an essential part of such objects - u always can check your expectation.
Stub and Mock a bit similar, but “There is a difference in that the stub uses state verification while the mock uses behavior verification.” M.Fowler
Dummy
Dummy objects - objects that are not use during tests and used only to allow compilation to be successful.
So, to summarize:
Fake - same functionality, but different implementation
Stub - provide predefined data
a. Spy - provide predefined data and logs of actions
Mock - register calls and can check your expectation
Dummy - allow tests to meet compiler requirements, not used in tests