-1

I am testing a class "DataSource" that extends another class "ExtendedDataSource". The method that I am testing in the "DataSource" class is calling another protected method in the "ExtendedDataSource" class.

class DataSource extends ExtendedDataSource {

    @Override
    protected Connection setup(Properties props) {
        Connection connection = super.getConnection(props);
        return connection;
    }
}

class ExtendedDataSource extends AbstractSource {

    @Override
    protected Connection getConnection(Properties props) {
        // here an user is retrieved from an abstract class that this class inherits
        // In this line I am getting an exception IllegalStateException--"user not set"
        String user = getUser();

        // Does something with props
        
        // Calls another protected method with the props as argument
        anothermethod(props); 
    }

    protected anothermethod(Properties props){
        //call another method that will do something with props 
    }
}

    public abstract class AbstractSource extends AnotherAbstractClass{
    
    public String getUser() {
        //the user as string
        return user;
    }
}

The test for the DataSource class is as follows :-

public class DataSourceTest {

    @InjectMocks
    DataSource dataSource;

    @Mock
    properties props;

    @Test
    public void datatest() {

        DataSource dataSource = new DataSource();

        dataSource.getConnection(props);

    }

}

How should I get over this situation? Should I also mock the Abstract class? But in this way do I have to mok all the classes?

Diego Borba
  • 1,282
  • 8
  • 22
Jeet
  • 359
  • 1
  • 6
  • 24
  • 1
    You already have a mocked `DataSource`. Just mock the `getUser` implementation from there. – Jorn Jul 28 '23 at 11:01
  • getUser implementation is in an abstract class that this class inherits. The abstract class is again inheriting another Abstract class. So like this do I need to mock all the abstract classes what is associated with the method? – Jeet Jul 28 '23 at 11:07
  • What? Your `DataSource` *is* an instance of those classes. You don't mock classes, you mock instances. – Jorn Jul 28 '23 at 11:08
  • @Jon - what do you mean by moccking the getUser() implementation? In the AbstractSource it only returns "return this.user;". I did the following-- Mock String User; AbstractSource abs=Mockito.mock(AbstractSource.class); when(abs.getUser).thenReturn(user); – Jeet Jul 28 '23 at 13:20

1 Answers1

0

If you're testing the DataSource class, you're not supposed to mock it. Usually you mock the dependencies of the class. The purpose is bringing the class you want to test to some state by injecting mocks of its dependencies, and then asserting the return values of its methods given the provided state. For example, if the constructor of DataSource looks like this:

public DataSource(Object o) {

    this.o = o;
}

Then you're supposed to mock o and then create an instance of DataSource with this mocked o. The test now is calling dataSource.setup(...), given the state that you mocked:

Object mock = Mockito.mock(Object.class);
DataSource dataSource = new DataSource(mock);
Assert.assertNotNull(dataSource.setup(...));

Now to go to your case - the issue that you're describing is exactly why when possible, I prefer composition over inheritance. My suggestion is that DataSource will get ExtendedDataSource (consider renaming it) in its constructor instead of extending it:

public DataSource(ExtendedDataSource extendedDataSource) {

    this.extendedDataSource = extendedDataSource;
}

Now you can mock ExtendedDataSource and setup some return value to ExtendedDataSource.getConnection, then create an instance of DataSource and pass the mock in the constructor, and assert the behavior of DataSource as you wanted:

ExtendedDataSource extendedDataSource = Mockito.mock(ExtendedDataSource.class);
Mockito.when(extendedDataSource.getConnection(...)).thenReturn(...);
DataSource dataSource = new DataSource(extendedDataSource);
Assert.assertNotNull(dataSource.setup(...));
idanz
  • 821
  • 1
  • 6
  • 17
  • Thanks for sharing the valuable inputs! I understand that when the object is injected to the constructor it becomes easier to test. I have mocked the props object and sent it as a parameter. The problem is with the setUrl() function. It is in an abstract class and giving IllegalStateException. But I have already mocked the props and sent it as an argument. – Jeet Jul 28 '23 at 11:25
  • I can't see `setUrl` in your post, but anyway - it sounds to me that `DataSource` is calling `setUrl`. Once again - mocking DataSource or any of its parent classes won't help you, since eventually you need to test a concrete instance of DataSource, so my recommendation - replace inheritance with composition, mock the abstract class and pass it to `DataSource` in the constructor, and then - test easily. – idanz Jul 28 '23 at 11:32
  • Sorry it is not setUrl rather getUser() which is in an abstract class. Furthermore, I cannot call extendedDataSource.getConnection() as the getConnection method is protected and cannot be called like that. – Jeet Jul 28 '23 at 11:56