Am using Java 1.8 and JUnit 5 to test a program which I wrote which grabs external zip files from a public url and then converts them into MD5 based hashes.
This post / question serves as not only a code review (but also contains a design & implementation question) for the class that is under test and the test case, itself.
JUnit 5 dependencies declared in pom.xml:
<properties>
<junit-jupiter.version>5.5.2</junit-jupiter.version>
</properties>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
FileHasher.java:
import java.io.InputStream;
import java.net.URL;
import java.security.DigestInputStream;
import java.security.MessageDigest;
public class FileHasher {
public static String makeHashFromUrl(String fileUrl) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
InputStream is = new URL(fileUrl).openStream();
try {
is = new DigestInputStream(is, md);
// Up to 8K per read
byte[] ignoredBuffer = new byte[8 * 1024];
while (is.read(ignoredBuffer) > 0) { }
} finally {
is.close();
}
byte[] digest = md.digest();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < digest.length; i++) {
sb.append(Integer.toString((digest[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
FileHasterTest.java (JUnit 5 test case):
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class FileHasterTest {
private String bsrFileUrl = "https://raw.githubusercontent.com/mlampros/DataSets/master/BSR_bsds500.zip";
private String fastTextFileUrl = "https://raw.githubusercontent.com/mlampros/DataSets/master/fastText_data.zip";
@Test
public void hashesAreTheSame() {
String hashedBsrFile1 = FileHasher.makeHashFromUrl(bsrFileUrl);
String hashedBsrFile2 = FileHasher.makeHashFromUrl(bsrFileUrl);
assertThat(hashedBsrFile1).isEqualTo(hashedBsrFile2);
}
@Test
public void hashesAreDifferent() {
String hashedBsrFile = FileHasher.makeHashFromUrl(bsrFileUrl);
String hashedFastTextFile = FileHasher.makeHashFromUrl(fastTextFileUrl);
assertThat(hashedBsrFile).isNotEqualTo(hashedFastTextFile);
}
@Test
public void hashIsNotNull() {
String hashedBsrFile = FileHasher.makeHashFromUrl(bsrFileUrl);
assertThat(hashedBsrFile).isNotNull();
}
@Test
public void hashedFileThrowsRuntimeExceptionWhenUrlIsNullOrEmpty() {
Assertions.assertThrows(RuntimeException.class, () -> {
String hashedNull = FileHasher.makeHashFromUrl(null);
});
Assertions.assertThrows(RuntimeException.class, () -> {
String hashedEmpty = FileHasher.makeHashFromUrl("");
});
}
}
Question(s):
Unit test specific:
Is this good test coverage (am I missing any edge cases)?
Is there a better way (e.g. am I missing something) to test my
FileHasherclass?Is there a better way to test when external file URL is empty or null?
Implementation / Design specific:
- Is throwing a RuntimeException seen as bad practice? If so, what's a better way to catch Exceptions in
FileHasher?