Search code examples
cassandrajunit4powermockitodatastax-java-driverspring-data-cassandra

How to mock the Data Stax Row object[com.datastax.driver.core.Row;] - Unit Test


Please find the below code for the DAO & Entity Object and Accessor

@Table(name = "Employee")
public class Employee {

    @PartitionKey
    @Column(name = "empname")
    private String empname;

    @ClusteringColumn(0)
    @Column(name = "country")
    private String country;

    @Column(name = "status")
    private String status;

}

Accessor:

@Accessor
public interface EmployeeAccessor { 

@Query(value = "SELECT DISTINCT empname FROM EMPLOYEE ")
ResultSet getAllEmployeeName();

}

}

DAO getAllEmployeeNames returns a List which are employee names and it will be sorted in ascending order. DAO

public class EmployeeDAOImpl implements EmployeeDAO {

    private EmployeeAccessor employeeAccessor;

        @PostConstruct
        public void init() {
            employeeAccessor = datastaxCassandraTemplate.getAccessor(EmployeeAccessor.class);
            }


    @Override
        public List<String> getAllEmployeeNames() {
            List<Row> names = employeeAccessor.getAllEmployeeName().all();
            List<String> empnames = names.stream()
                    .map(name -> name.getString("empname")).collect(Collectors.toList());
            empnames.sort(naturalOrder()); //sorted
            return empnames;
        }

    }   

JUnit Test(mockito):

I am not able to mock the List[datastax row]. How to mock and returns a list of rows with values "foo" and "bar".Please help me in unit test this.

@Category(UnitTest.class)
@RunWith(MockitoJUnitRunner.class)
public class EmployeeDAOImplUnitTest {  

    @Mock
    private ResultSet resultSet;

    @Mock
    private EmployeeAccessor empAccessor;

    //here is the problem....how to mock the List<Row> Object -->  com.datastax.driver.core.Row (interface)
    //this code will result in compilation error as we are mapping a List<Row> to the ArrayList<String>
    //how to mock the List<Row> with a list of String row object
    private List<Row> unSortedTemplateNames = new ArrayList() {
            {
                add("foo");
                add("bar");
            }
        };

        //this is a test case to check if the results are sorted or not
        //mock the accessor and send rows as "foo" & "bar"
        //after calling the dao , the first element must be "bar" and not "foo"
        @Test
            public void shouldReturnSorted_getAllTemplateNames() {
                when(empAccessor.getAllEmployeeName()).thenReturn(resultSet);
                when(resultSet.all()).thenReturn(unSortedTemplateNames); //how to mock the List<Row> object ???
                //i am testing if the results are sorted, first element should not be foo
                assertThat(countryTemplates.get(0), is("bar"));
            }   

}

Solution

  • Wow! This is overly complex, hard to follow, and not an ideal way to write unit tests.

    Using PowerMock(ito) along with "static" references in your own code is not recommended and is a sure sign of a code smells.

    First, I am not sure why you decided to use a static reference (e.g. EmployeeAccessor.getAllEmployeeName().all(); inside the EmployeeDAOImpl class, getAllEmployeeNames() method) instead of using the instance variable (i.e. empAccessor), which is more conducive to actual "unit testing"?

    The EmployeeAccessor, getAllEmployeeName() "interface" method is not static (clearly). However, seemingly, whatever this (datastaxCassandraTemplate.getAccessor(EmployeeAccessor.class);) generates makes it so (really?), which then requires the use of PowerMock(ito), o.O

    Frameworks like PowerMock, and extensions of (i.e. "PowerMockito"), were meant to test and mock code used by your application (unfortunately, but necessarily so) where this "other" code makes use of statics, Singletons, private methods and so on. This anti-pattern really ought not be followed in your own application design.

    Second, it is not really apparent what the "Subject Under Test" (SUT) is in your test case. You implemented a test class (i.e. EmployeeDAOImplTest) for, supposedly, your EmployeeDAOImpl class (the actual "SUT"), but inside your test case (i.e. shouldReturnSorted_getAllTemplateNames()), you are calling... countryLocalizationDAOImpl.getAllTemplateNames(); thus testing the CountryLocalizationDAOImpl class (??), which is not the "SUT" of the EmployeeDAOImplTest class.

    Additionally, it is not apparent that the EmployeeDAOImpl even uses a CountryLocalizationDAO instance (assuming an interface here as well), and if it does, then it is certainly something that should be "mocked" when the EmployeeDAOImpl "interacts" with instances of CountryLocalizationDAO, particularly in the context of a unit test. The only correlation between the EmployeeDAO and CountryLocalizationDAO is that the Employee has a country field.

    There are a few other problems with your design/setup as well, but anyway.

    Here are a few suggestions...

    First, let's test what your EmployeeDAOImplTest is meant to test... EmployeeDAO.getAllEmployeeNames() in a sorted fashion. This in turn may give you ideas of how to test your "CountryLocalizationDAO, getAllTemplateNames() method perhaps (if it even makes sense, i.e. getAllTemplateNames() is in fact dependent on an Employee's country, when Employees are ordered by name (i.e. "empname" and accessed via EmployeeAccessor).

    public class EmployeeDAOImpl implements EmployeeDAO {
    
        private final EmployeeAccessor employeeAccessor;
    
        // where does the DataStaxCassandraTemplate reference come from?!
        private DataStaxCassadraTemplate datastaxCassandraTemplate = ...;
    
        public EmployeeDAOImpl() {
            this(datastaxCassandraTemplate.getAccessor(EmployeeAccessor.class));
        }
    
        public EmployeeDAOImpl(EmployeeAccessor employeeAccessor) {
            this.employeeAccessor = employeeAccessor;
        }
    
        protected EmployeeAccessor getEmployeeAccessor() {
            return this.empAccessor;
        }
    
        public List<String> getAllEployeeNames() {
            List<Row> nameRows = getEmployeeAccessor().getAllEmployeeName().all();
            ...
        }
    }
    

    Then in your test class...

    public class EmployeeDAOImplUnitTest { 
    
        @Mock
        private EmployeeAccessor mockEmployeeAccessor;
    
        // SUT
        private EmployeeDAO employeeDao;
    
        @Before
        public void setup() {
            employeeDao = new EmployeeDAOImpl(mockEmployeeAccessor);
        }
    
        protected ResultSet mockResultSet(Row... rows) {
            ResultSet mockResultSet = mock(ResultSet.class);
            when(mockResultSet.all()).thenReturn(Arrays.asList(rows));
            return mockResultSet;
        }
    
        protected Row mockRow(String employeeName) {
            Row mockRow = mock(Row.class, employeeName);
            when(mockRow.getString(eq("empname")).thenReturn(employeeName);
            return mockRow;
        }
    
        @Test
        public void getAllEmployeeNamesReturnsSortListOfNames() {
            when(mockEmployeeAccessor.getAllEmployeeName())
                .thenReturn(mockResultSet(mockRow("jonDoe"), mockRow("janeDoe")));
    
            assertThat(employeeDao.getAllEmployeeNames())
                .contains("janeDoe", "jonDoe");
    
            verify(mockEmployeeAccessor, times(1)).getAllEmployeeName();
        }
    }
    

    Now, you can apply similar techniques if in fact there is an actual correlation between Employees and CountryLocalizationDAO via the EmployeeAccessor.

    Hope this helps get you on a better track!

    -j