如何模拟 Data Stax Row 对象 [com.datastax.driver.core.Row;] - 单元测试

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

请找到以下 DAO 和实体对象和访问器的代码

@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
public interface EmployeeAccessor { 

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

}

}

DAO getAllEmployeeNames returns 员工姓名列表 它将按升序排序。 道

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 测试(mockito):

我无法模拟列表[datastax 行]。如何模拟和 returns 具有值 "foo" 和 "bar" 的行列表。请帮助我对此进行单元测试。

@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"));
            }   

}

哇!这过于复杂,难以遵循,并且不是编写单元测试的理想方式。

不推荐在您自己的代码中使用 PowerMock(ito) 和 "static" 引用,这肯定是代码有异味的标志。

首先,我不确定您为什么决定使用静态引用(例如 EmployeeAccessor.getAllEmployeeName().all();EmployeeDAOImpl class、getAllEmployeeNames() 方法中)而不是使用实例变量(即empAccessor),更有利于实际"unit testing"?

EmployeeAccessor, getAllEmployeeName() "interface" 方法不是静态的(显然)。然而,表面上看,无论这个 (datastaxCassandraTemplate.getAccessor(EmployeeAccessor.class);) 生成什么都会使它如此(真的吗?),然后需要使用 PowerMock(ito)、o.O

PowerMock 等框架及其扩展(即“PowerMockito”)旨在测试和模拟您的应用程序使用的代码(不幸的是,但必然如此)此 "other" 代码使用了静态、单例、私有方法等。在您自己的应用程序设计中确实不应该遵循这种反模式。

其次,“被测对象”(SUT)在您的测试用例中的含义并不明显。您实施了一个测试 class(即 EmployeeDAOImplTest),据推测,您的 EmployeeDAOImpl class(实际的 "SUT"),但在您的测试用例中(即 shouldReturnSorted_getAllTemplateNames()), 你正在调用... countryLocalizationDAOImpl.getAllTemplateNames(); 从而测试 CountryLocalizationDAOImpl class (??), 这不是 [=19= 的 "SUT" ] class.

此外,EmployeeDAOImpl 甚至使用了一个 CountryLocalizationDAO 实例(假设这里也有一个接口)并不明显,如果确实如此,那么它肯定应该是 "mocked" 当 EmployeeDAOImpl "interacts" 带有 CountryLocalizationDAO 的实例时,特别是在单元测试的上下文中。 EmployeeDAOCountryLocalizationDAO 之间的唯一关联是 Employee 有一个 country 字段。

你的 design/setup 还有一些其他问题,但无论如何。

这里有一些建议...

首先,让我们以有序的方式测试一下您的 EmployeeDAOImplTest 是要测试什么... EmployeeDAO.getAllEmployeeNames()。这反过来可能会让您了解如何测试您的“CountryLocalizationDAOgetAllTemplateNames() 方法(如果它甚至有意义,即 getAllTemplateNames() 实际上依赖于 Employee's country,当 Employees 按名称排序时(即“empname”并通过 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();
        ...
    }
}

那么在你的测试中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();
    }
}

现在,如果 EmployeesCountryLocalizationDAO 通过 EmployeeAccessor.

之间存在实际相关性,您可以应用类似的技术

希望这可以帮助您走上更好的轨道!

-j