Testing – Test Doubles

The topic tries to cover high level of Test Doubles, Mocks. Here we have many named concepts around, Fake, Mock, Spy, Stub, etc…

Testing with mocks, stubs is primarily happens with unit, integration testing.
In object oriented world, bigger or smaller units of codes are tested, and while we having SRP objects, we still have to deal with dependencies and other object structures while we processing code.
To clean cut and provide a stable state we’re should fix those parameters/dependencies, which are not in the question from the testing perspective.

Theory on the Test Doubles

Let’s see a black-box case on signature level.

  • Objects a and b with methods a.foo(..), b.bar()
  • a(b), a contains b as constructor dependency
  • the method in question to test: a.foo(x) : y

So while y output depends on x input parameter, there is the possibility our result also depends on b / b.bar()
Here comes the idea to fix b.bar(), so we can make sure only x has effect on y.
By replacing b we’re able to isolate and focus on the code being tested and not on the behavior or state of external dependencies.
We should replicate the object b, to get a return value as a real b object would do, but this should be ideally a fixed, predefined value.

b is replaceable by satisfying the contract, which is ideally an interface, while the correctness should remain.

Prove on replacing – correctness, see Liskov Substitution Principle – there are criticism on LSP if we consider super-subtype, however if we look at interface-implementation level, the interface contract exactly defines the behavior, anyhow the small details of implementation changes.

Type of Test Doubles

I get Martin Fowler List of Test Doubles

  • Dummy
    simple, non-production object. Used solely to replace unused inputs. Most simpler example: DummyRestApiExample
  • Fake
    real objects, but simplified only with data or objects with very limited, partial functionality
    examples: file-fake-java
  • Stub
    stubs are usually dumb, fixed with static values. It return a specific result, based on a specific set of inputs. Usually provides bigger, but more rigid set of results or structure compared to mocks.
    With it you can primarily verify state.
    example: during TDD you’ll create and empty shell with hard-coded return statements to replicate a non-existing service.
  • Mock
    replaces the whole object with the possibility of dynamic behavior (varied on call times, varied by parameters and even ability to verify calls)
    methods either overridden with static value or logic
    on undefined it returns either with exception, default value, etc.
    With it you can verify not just state, but rather behavior.
    example: mock object, returns with a fixed value or small logical answer, then you verify the call and another input parameters
    example: InputPopulatorActionTests.java
  • Spy – partially replacing methods, half mocks
    methods overridden with static value or logic
    on undefined it’ll use/invoke the original Object method
    example: MockitoSpyUnitTest.java, AbstractPageObjectTests.java

Other Tools for Testing

You can always capitalize on Builders, Mock builders provide an easy way to create similar mock in a dynamic manner and makes the code more fluent: PizzaUnitTest.java
With Fluent interface driven builder you can even build more complex logic.

Object Mothers are helping to create example data filled objects. This helps great in code reuse

// https://blog.codeleak.pl/2014/06/test-data-builders-and-object-mother.html
public class TestUsers {
    public static User aUser() {
        return new User("John Smith", "jsmith", "42xcc", "ROLE_USER");
    }
    // other factory methods
}
User user = TestUsers.aUser();

And finally Know Your Tools
As an example from Mockito, if you need a long chain of object calls, maybe you don’t need to build a whole structure of objects.
Mockito.html#RETURNS_DEEP_STUBS

Foo mock = mock(Foo.class, RETURNS_DEEP_STUBS);
// note that we're stubbing a chain of methods here: getBar().getName()
when(mock.getBar().getName()).thenReturn("deep");

Sources:

https://lib.dr.iastate.edu/cgi/viewcontent.cgi?article=1249&context=cs_techreports
https://martinfowler.com/bliki/TestDouble.html
https://martinfowler.com/articles/mocksArentStubs.html
http://xunitpatterns.com/Test%20Double.html
https://blog.codeleak.pl/2014/06/test-data-builders-and-object-mother.html