Java 如何增加与数据库交互的类的测试覆盖率?

Java 如何增加与数据库交互的类的测试覆盖率?,java,mysql,junit,test-coverage,Java,Mysql,Junit,Test Coverage,我得到了一个名为PatientRepositoryImpl的Java类,它包含一些方法,可以插入、删除或更新MySql数据库中的数据。 我还为这个类编写了一些单元测试。 当我检查测试的覆盖率时,我只得到59%,尽管几乎每一行代码都被覆盖工具标记为绿色,除了SQL异常。 我是新来的,希望我做的一切都是对的,如果有人能帮助我,我将非常感激 这里是我的存储库和测试的代码 public class PatientRepositoryMySqlImpl implements PatientReposito

我得到了一个名为PatientRepositoryImpl的Java类,它包含一些方法,可以插入、删除或更新MySql数据库中的数据。 我还为这个类编写了一些单元测试。 当我检查测试的覆盖率时,我只得到59%,尽管几乎每一行代码都被覆盖工具标记为绿色,除了SQL异常。 我是新来的,希望我做的一切都是对的,如果有人能帮助我,我将非常感激

这里是我的存储库和测试的代码

public class PatientRepositoryMySqlImpl implements PatientRepository {


private DatabaseConnection connection;
private PatientGenerator patientGenerator;


public PatientRepositoryMySqlImpl(DatabaseConnection connection, PatientGenerator patientGenerator) {
    this.connection = connection;
    this.patientGenerator = patientGenerator;
}

/* (non-Javadoc)
 * @see com.id.hl7sim.PatientRepository#insertPatient()
 */
@Override
public void insertPatient(Patient patient) {
    if (!patient.isValid()) { 
        throw new IllegalArgumentException("Incomplete Patient");
    } else {
        String insert = "INSERT INTO tbl_patient(lastname, firstname, gender, birthday) VALUES('"
        + patient.getLastname() + "', '" + patient.getFirstname() + "', '" + patient.getGender() + "', '"
        + patient.getBirthday().toString() + "');";
        try (Connection dbConnection = connection.getDBConnection();
             Statement statement = dbConnection.prepareStatement(insert)) {
            statement.executeUpdate(insert);
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
    }
}

/* (non-Javadoc)
 * @see com.id.hl7sim.PatientRepository#insertListOfPatients()
 */
@Override
public void insertListOfPatients(List<Patient> allPatients) {
    for (Patient patient : allPatients) {
        insertPatient(patient);
    }
}

/* (non-Javadoc)
 * @see com.id.hl7sim.PatientRepository#getRandomPatient()
 */
@Override
public Patient getRandomPatient() {
    Patient patient = new Patient.Builder().build();
    String query = "SELECT * FROM tbl_patient ORDER BY RAND() LIMIT 1";
    try (Connection dbConnection = connection.getDBConnection();
         Statement statement = dbConnection.createStatement();) {
        ResultSet rs = statement.executeQuery(query);
        rs.next();
        setPatientBasicData(patient, rs);
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
    return patient;
}

private void setPatientBasicData(Patient patient, ResultSet rs) {
    try {
        patient.setId(rs.getInt("id"));
        patient.setLastname(rs.getString("lastname"));
        patient.setFirstname(rs.getString("firstname"));
        patient.setGender(rs.getString("gender"));
        patient.setBirthday(parseBirthday(rs.getString("birthday")));
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
}

public LocalDate parseBirthday(String birthday) {
    LocalDate localDate = LocalDate.parse(birthday);
    return localDate;
}

/* (non-Javadoc)
 * @see com.id.hl7sim.PatientRepository#admitRandomPatient()
 */
@Override
public Patient admitRandomPatient() {
    Patient patient = getRandomPatient();
    patient.setDepartment(patientGenerator.getRandomDepartment());
    patient.setWard(patientGenerator.getRandomWard());
    patient.setAdmissionDateTime(LocalDateTime.now());
    patient.setStatus("I");
    String insert = "INSERT INTO tbl_inpatients(id, ward, department, admissionDate, patientStatus) VALUES('"
                    + patient.getId() + "', '" + patient.getWard() + "', '" + patient.getDepartment() + "', '"
                    + patient.getAdmissionDateTime().toString() + "', '" + patient.getStatus() + "')";
    try (Connection dbConnection = connection.getDBConnection();
         PreparedStatement statement = dbConnection.prepareStatement(insert)) {
        statement.executeUpdate(insert, Statement.RETURN_GENERATED_KEYS);
        ResultSet keys = statement.getGeneratedKeys();
        keys.next();
        patient.setCaseId(keys.getInt(1));
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
    return patient;
}

public Patient getRandomInpatient() {
    Patient patient = new Patient.Builder().build();
    String query = "SELECT * FROM tbl_inpatients ip, tbl_patient p WHERE p.id = ip.id  ORDER BY RAND() LIMIT 1";
    try (Connection dbConnection = connection.getDBConnection();
         Statement statement = dbConnection.createStatement();) {
        ResultSet rs = statement.executeQuery(query);
        rs.next();
        setPatientBasicData(patient, rs);
        setPatientCaseData(patient, rs);
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
    return patient;
}

public void setPatientCaseData(Patient patient, ResultSet rs) {
    try {
        patient.setWard(rs.getString("ward"));
        patient.setDepartment(rs.getString("department"));
        patient.setAdmissionDateTime(parseLocalDateTime(rs.getString("admissionDate")));
        patient.setStatus(rs.getString("patientStatus"));
        patient.setCaseId(rs.getInt("case"));
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
}

public LocalDateTime parseLocalDateTime(String localdatetime) {
    localdatetime = localdatetime.replace("T", "");
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss.SSS");
    LocalDateTime formattedLocalDateTime = LocalDateTime.parse(localdatetime, formatter);
    return formattedLocalDateTime;
}

/* (non-Javadoc)
 * @see com.id.hl7sim.PatientRepository#transferRandomPatient()
 */
@Override
public Patient transferRandomPatient() {
    Patient patient = getRandomInpatient();
    patient.setPriorWard(patient.getWard());
    patient.setPriorDepartment(patient.getPriorDepartment());
    patient.setDepartment(patientGenerator.getRandomDepartment());
    patient.setWard(patientGenerator.getRandomWard());
    String update = "UPDATE tbl_inpatients SET ward='" + patient.getWard() + "', department='"
            + patient.getDepartment() + "' WHERE id='" + patient.getId() + "'";
    try (Connection dbConnection = connection.getDBConnection();
         Statement statement = dbConnection.prepareStatement(update)) {
        statement.executeUpdate(update);
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
    return patient;
}

/* (non-Javadoc)
 * @see com.id.hl7sim.PatientRepository#dischargeRandomPatient()
 */
@Override
public Patient dischargeRandomPatient() {
    Patient patient = getRandomInpatient();
    patient.setDischargeDateTime(LocalDateTime.now());
    insertFormerPatient(patient);
    String delete = "DELETE FROM tbl_inpatients WHERE `case`=" + patient.getCaseId();
    try (Connection dbConnection = connection.getDBConnection();
         Statement statement = dbConnection.prepareStatement(delete)) {
        statement.executeUpdate(delete);
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
    return patient;
}

public void insertFormerPatient(Patient patient) {
    String insert = "INSERT INTO tbl_formerpatients(`case`, `id`, ward, department, admissionDate, dischargeDate) VALUES('"
            + patient.getCaseId() + "', '" + patient.getId() + "', '" + patient.getWard() + "', '"
            + patient.getDepartment() + "', '" + patient.getAdmissionDateTime().toString() + "', '"
            + patient.getDischargeDateTime().toString() + "')";
    try (Connection dbConnection = connection.getDBConnection();
         Statement statement = dbConnection.prepareStatement(insert)) {
        statement.executeUpdate(insert);
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
}

public int countInpatients() {
    int numberOfPatients = 0;
    String query = "SELECT COUNT(id) AS numberOfPatients FROM tbl_inpatients";
    try (Connection dbConnection = connection.getDBConnection();
            Statement statement = dbConnection.createStatement();) {
        ResultSet rs = statement.executeQuery(query);
        while (rs.next()) {
            numberOfPatients = rs.getInt(1);
        }
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
    return numberOfPatients;
}

public int countPatients() {
    int numberOfPatients = 0;
    String query = "SELECT COUNT(id) AS numberOfPatients FROM tbl_patient";
    try (Connection dbConnection = connection.getDBConnection();
            Statement statement = dbConnection.createStatement();) {
        ResultSet rs = statement.executeQuery(query);
        while (rs.next()) {
            numberOfPatients = rs.getInt(1);
        }
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
    return numberOfPatients;
}

虽然我完全同意,为了增加覆盖率,您肯定应该“模拟”出问题并抛出
SQLException
s的场景,但让我介绍另一种方法,它也将回答这个问题,但希望能给您提供另一种视角

JDBC相当麻烦,而抛出SQL异常的测试可能不是最令人愉快的测试。此外,我发现您并没有真正处理异常,只是将它们记录在控制台中

所以,也许你应该试着不要直接与JDBC合作,而是使用一些能帮你解决JDBC使用问题的库。例如,对于这样一个库,看一看,我知道这是一个非常古老的东西,但是,嘿,直接使用JDBC也可能不是最现代的方法,所以我尝试进行更少的更改,但仍然获得了价值。此外,有人可能会说,这是一个古老而不奇特的图书馆,我可以说,它是一个经过战斗考验的图书馆,甚至没有春天本身

现在,由于它包装了JDBC异常处理,重点是您根本不必涵盖这些情况。所以你的保险范围自然会增加


当然,也存在其他低级别和不太低级别的替代方案(例如,或仅举几个例子),但这是另一种情况,所有这些方案都会在您提出的问题的意义上增加覆盖率。

使用抛出异常的模拟对于单元测试,您至少应该模拟数据库连接。您不需要验证数据是否写入数据库,只需要验证是否发出了正确的语句;DBConnection的单元(或集成)测试需要验证这些测试是否产生了正确的结果。非常感谢您!我是一个新的嘲弄者,但我在这里得到了很多信息并尝试了一些东西。你能看看我的编辑吗,你是说这样吗?非常感谢你@Mark Bramnik,我感谢你的帮助,现在我会在我的项目上尝试这个。好吧,这花了我一些时间,但最后我完成了所有的更改。我的覆盖范围增加得很好,我还认为,使用SpringJDBC模板的解决方案非常棒,我喜欢它-谢谢!
public class PatientRepositoryMySqlImplTest {

PatientRepository testPatientRepository;

Patient testPatient;

Patient testPatientTwo;

List<Patient> testBothPatients;

DatabaseConnection testConnection;

PatientGenerator testPatientGenerator;

Firstnames testFirstnames;

Lastnames testLastnames;

Departments testDepartments;

Wards testWards;

@Before
public void setUp() throws Exception {

    testDepartments = JAXB.unmarshal(ClassLoader.getSystemResource("departments.xml"), Departments.class);
    testWards = JAXB.unmarshal(ClassLoader.getSystemResource("wards.xml"), Wards.class);
    testLastnames = JAXB.unmarshal(ClassLoader.getSystemResource("lastnames.xml"), Lastnames.class);
    testFirstnames = JAXB.unmarshal(ClassLoader.getSystemResource("firstnames.xml"), Firstnames.class);

    testPatientGenerator = new PatientGeneratorImpl(testFirstnames, testLastnames, testDepartments, testWards);

    testPatient = testPatientGenerator.randomizeNewPatient();

    testPatientTwo = testPatientGenerator.randomizeNewPatient();

    testBothPatients = new ArrayList<Patient>();

    testConnection = new MySqlConnection();

    testPatientRepository = new PatientRepositoryMySqlImpl(testConnection, testPatientGenerator);

    testPatientRepository.admitRandomPatient();

}

@Test
public void testAdmitRandomPatient() {

    testPatient = testPatientRepository.admitRandomPatient();

    assertTrue(testPatient.isValid());
}

@Test
public void testGetRandomInpatient() {

    testPatient = testPatientRepository.getRandomInpatient();

    assertTrue(testPatient.isValid());
}

@Test
public void testDischargeRandomPatientValid() {

    testPatient = testPatientRepository.dischargeRandomPatient();

    assertTrue(testPatient.isValid());
    assertTrue(testPatient.getCaseId() != 0);
}

@Test
public void testDischargeRandomPatientDatabase() {

    int beforeDischarge = testPatientRepository.countInpatients();
    testPatient = testPatientRepository.dischargeRandomPatient();
    int afterDischarge = testPatientRepository.countInpatients();

    assertTrue(afterDischarge == beforeDischarge - 1);

}

@Test(expected = IllegalArgumentException.class)
public void testInsertPatientWitIncompletePatient() {

    testPatient.setFirstname("");

    testPatientRepository.insertPatient(testPatient);
}

@Test
public void testTransferRandomPatient() {

    testPatient = testPatientRepository.transferRandomPatient();

    assertTrue(testPatient.getDepartment() != testPatient.getPriorDepartment());
}

@Test
public void testInsertPatient() {

    int numberOfPatients = testPatientRepository.countInpatients();

    testPatientRepository.insertPatient(testPatient);

    assertTrue(testPatientRepository.countInpatients() >= numberOfPatients);
}

@Test
public void testInsertListOfPatients() {

    testBothPatients.add(testPatient);
    testBothPatients.add(testPatientTwo);

    int countInpatientsBeforeInsertion = testPatientRepository.countPatients();

    testPatientRepository.insertListOfPatients(testBothPatients);

    int countInpatientsAfterInsertion = testPatientRepository.countPatients();
    assertTrue(countInpatientsAfterInsertion > countInpatientsBeforeInsertion);

}
@Test
public void mockThrowsException() {

    PatientRepository testPatientRepository = mock(PatientRepositoryMySqlImpl.class);

    when(testPatientRepository.getRandomPatient()).thenThrow(SQLException.class);

    testPatientRepository.admitRandomPatient();

}