1

I claim that my testing is failing because my tested class isn't seeing mocks. My evidence:

  • In the debugger my objects have ordinary java.sql.* parents. They aren't of Mockito.*.

  • When I try stepping into objects like DbSql I see actual DbSql code, not Mockito code.

  • When I run, my executeQuery() throws a NullPointerException.

Here are parts of my test class:

//  Test Class
//  My mocks:
HostMessage mockHostMessage;
DbConnect mockDbConnect;
DbCache mockDbCache;
DbSql mockDbSql;
ResultSet mockResultSet;

@BeforeEach
void setUp()
{
  mockHostMessage     = Mockito.mock(HostMessage.class);
  mockDbConnect       = Mockito.mock(DbConnect.class);
  mockDbCache         = Mockito.mock(DbCache.class);
  mockDbSql           = Mockito.mock(DbSql.class);
  mockResultSet       = Mockito.mock(ResultSet.class);
}

@Test
void myTest() {
  String errMsg = "something";

  try {
    doReturn(mockDbSql).when(mockDbCache).getDbSql(any(), anyString());
    doReturn(mockResultSet).when(mockDbSql).executeQuery();
    doReturn(false).when(mockResultSet).next();
    (new CartonRoutingProcessor()).process(mockDbConnect, mockHostMessage);
  } catch(Throwable t) {
    
    if(t instanceof MockitoException) {
      fail(t.getMessage());
    } else if(t instanceof SqlException) {
      Assertions.assertTrue(t.getMessage().contains(errMsg),
           "Phrase not found in SqlException message: " + errMsg);
      fail(t.getMessage());
    } else {
      fail(t.getMessage());
    }
    
  }

Here are parts of my CartonRoutingProcessor business logic:

public boolean process(DbConnect conn, HostMessage msg) throws SQLException
{
  try(DbCache dbc = new DbCache())
  {
    DbSql mySql = dbc.getDbSql(conn, String.format("select id from routes"));
    
    // Throws NullPointerException right here, 
    // while trying to execute the query rather than mock it.
    ResultSet rs = mySql.executeQuery();

    String routeType = rs.next() ? rs.getString("id") : null;
    ...
  }
  ...
}

The Mockito manual, and lots of web pages, tell how to do this task.

Yet I'm not getting the results I'm told I should have.

Can a kind developer give me a clue?

Jerome M
  • 19
  • 2

1 Answers1

1

Here you are using real implementation not a mock

  try(DbCache dbc = new DbCache())
  {
   ///dbc is a real instance of DbCache()
    DbSql mySql = dbc.getDbSql(conn, String.format("select id from routes"));

therfore your mocks are unused in your particular test scenario with given implementation.

When I see a mock that returns a mock that returns a mock (and so on, you get the idea) I start to feel that either code is not testable, or my test approach is bad. After all, what do you want to test here?

Lets say you want to verify the calling layers behaves in response to true/false result of the process method.In such case, whole process can be mocked/spyOn and responses can be created artificialy so you can focus on that particular feature - response to a reult

On the other hand, you might want to test how process merhod actually works and if route type is fetched correctly - but then you should rather use actualt DB (could be inmemory like H2 for example) to verify that you interact with the DB correcly, or you could have whole fetching in another method that will be spied on.

The correct approach depends on actual goal of the test, and its scope (unit tests have very narrow scope - testing eg single method).

In your case, since not all of the "pieces" can be provided from the outside(as mocks in that case) I would stick with inmemory db - less stubbing to be done. The drawback is that now you have to prepare actual data in the DB in order to setup the scenario which might be cumbersome.

Antoniossss
  • 31,590
  • 6
  • 57
  • 99
  • 1
    Q: Please consider updating your post and suggest alternatives for the OP to use the mock object – paulsm4 Oct 14 '22 at 21:05
  • I've chained mocks together before. Sometimes the mocked service produces an intermediate that you also have to mock out. Nothing wrong with that, IMO. – Makoto Oct 14 '22 at 22:11
  • The code with the "new DbCache()" is the business logic that demands testing. I can't casually rewrite its use without getting management to do some more acceptance testing on customer equipment. Yes, I'm adding testing after the fact. – Jerome M Oct 17 '22 at 01:39
  • I'll try again, mocking a constructor for DbCache. Regarding other comments, it may be that the method as-written *is* untestable. I'm starting to think that TDD is a *good* thing, building testability into the design! – Jerome M Oct 17 '22 at 01:41
  • What am I trying to test? Each decision branch, without reference to the DB itself. I'm testing just the process() unit, not an integration test w/ DB. For example, I'll test if the fetched routeType is null. However, I will examine if I've a different approach to mocking, based on comments here. – Jerome M Oct 17 '22 at 01:44
  • But in order to test "Every branch" you donta have to go from the start to the begining using every possible permutation of branching along the way. In your case I would say that `process` shoud be mocked to return arbitrary true/false and study the response of calling layer. – Antoniossss Oct 17 '22 at 06:15