Проблема с тестируемостью почти всегда одна и та же: без внедрения зависимостей. Этот случай ничем не отличается. Здесь конкретная проблема заключается в том, что вы не можете просто предоставить URL-адрес file://...
, потому что вы используете Apache Commons Validate, который выдает false
для isValid(url)
.
Решение состоит в том, чтобы позволить вашему классу внедрить UrlValidator
через установщик или дополнительный конструктор. Я выбираю подход сеттера в моем примере кода. Я даже сделал сеттер с областью действия пакета, если вам не нравится общедоступный сеттер, поэтому вы можете просто поместить свой тест в тот же пакет, что и исходный класс (что в любом случае является обычной практикой), чтобы получить к нему доступ.
Теперь просто вставьте макет во время теста и с радостью используйте URL файла:
package de.scrum_master.stackoverflow;
import org.apache.commons.validator.routines.UrlValidator;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.zip.GZIPInputStream;
public class DownloadFile {
private UrlValidator urlValidator = UrlValidator.getInstance();
void setUrlValidator(UrlValidator urlValidator) {
this.urlValidator = urlValidator;
}
public BufferedReader downloadGzipCsvFile(String url) throws MalformedURLException {
BufferedReader br = null;
if (urlValidator.isValid(url)) {
try {
br = new BufferedReader(
new InputStreamReader(
new GZIPInputStream(
new URL(url).openStream()
)
)
);
} catch (IOException e) {
e.printStackTrace();
System.exit(0);
}
}
else {
throw new MalformedURLException("Supplied URL: " + url + " is an invalid URL");
}
return br;
}
}
package de.scrum_master.stackoverflow
import org.apache.commons.validator.routines.UrlValidator
import spock.lang.Specification
class DownloadFileSpec extends Specification {
def "Should return buffered reader for url for gzip csv file"() {
given:
String url = new File("src/test/resources/test_data.csv.gz").toURI().toURL()
def downloadFile = new DownloadFile()
downloadFile.urlValidator = Mock(UrlValidator) {
isValid(_) >> true
}
when:
def bufferedReader = downloadFile.downloadGzipCsvFile(url)
then:
bufferedReader.ready()
}
}
Кстати, тест также показывает, как преобразовать экземпляр File
, созданный из относительного пути, в URL файла.
P.S .: Вы также можете использовать PowerMock для макетирования статических методов или конструкторов, что также решит проблему. Но я считаю, что всякий раз, когда вам нужен PowerMock, это запах кода, и вы должны рефакторинг, что я и сделал для вас.