1

I was reading this article on Unit Testing. It seems pretty straightforward, but there was a section that interested me and I wanted to see if someone could please provide an explanation or example of what this means. I think I understand it but maybe not well enough.

Write test cases that are independent of each other. For example, if a class depends on a database, do not write a case that interacts with the database to test the class. Instead, create an abstract interface around that database connection and implement that interface with a mock object.

What does it mean to:

  1. create an abstract interface around the db connection?
  2. then implement it with a mock object?

I am more questioning the first part (1) but if someone can explain both parts that would be helpful. Thanks.

saeed foroughi
  • 1,662
  • 1
  • 13
  • 25
sgx
  • 1,265
  • 3
  • 19
  • 36
  • 4
    Create a data-access-object which hides the fact that you use JDBC (or JPA, or ...) and when testing mock or stub this data-access-object so that you can test without an actual database. Unless you are writing an integration test ofcourse. – M. Deinum Jan 28 '20 at 07:27
  • What do you mean by mock or stub? – sgx Jan 28 '20 at 07:30
  • 3
    Start here: https://stackoverflow.com/questions/383947/what-does-it-mean-to-program-to-an-interface – chrylis -cautiouslyoptimistic- Jan 28 '20 at 07:33
  • 1
    You can see a mock as a dummy implementation of something. A mock of a database is a simple test class that for example would output Y when you send a query X to it. Instead of going to an actual database, this logic is written in Java, so you are independent from any external sources for your testing. There is also no use in testing external software like a DB. – Wesley De Keirsmaeker Jan 28 '20 at 07:40
  • 2
    @WesleyDeKeirsmaeker To be more precise, a mock is not just a "dummy implementation", it is an object without behaviour (other than the mocking mechnisms). Instead of actually searching a database, a mock will simply return the object you told it to return for a certain call. A stub on the other hand *does* have behaviour, but a simplified one. Also there is *spies* and *captors* to collect information about a dependency's use. At least in Mockito mocks are always spies, too. If possible, prefer mocks over stubs and spying on real objects. – Amadán Jan 28 '20 at 07:55

1 Answers1

1

ONE: The "abstract data interface" simply means that you provide an interface with methods for storing and finding data in a business oriented way, rather than use the database connection directly. You can then implement this interface to store data in different ways: an sql database, a file based approach, etc. Such a class in some patterns is also referred to as "data access object" (DAO).

public interface PersonDao {
    void store(Person personToStore);
    Person findById(String id);
}

public class SqlPersonDao implements PersonDao {
    @Override
    void store(Person personToStore) {
        // use database connection here ...
    }
}

Basically in a unit test you always want to mock anything that is not your system under test and has a complex behaviour you cannot control. That is especially true for things like system time, for example if a class uses system time, for tests you want a way to inject a predefined time overriding the system clock.

TWO:

In unit tests you don't want to be affected by bugs in any dependency. For a unit using the PersonDao, the PersonDaowould be such a dependency. Rather than relying on the real implementation's behaviour, you want to exactly define the results you expect (using the notation of the Mockito mocking framework and the AssertJ validation framework here):

class MyUnitTest {

    // system under test
    MyUnit sut;

    @Mock
    PersonDao personDaoMock;

    @BeforeEach
    public setup() {
        initMocks(this);
        sut = new MyUnit("some", "parameters");
    }

    @Test
    void myTest() {
        // setup test environment using a mock
        var somePerson = new Person("101", "John", "Doe");
        doReturn(somePerson).when(personDaoMock.findById("101"));

        // run test
        var actualValue = sut.doSomething();

        // check results
        assertThat(actualValue).isNotNull();
    }
}
Amadán
  • 718
  • 5
  • 18