Сообщение довольно очевидно: вы не можете высмеивать невидимые и окончательные классы. Короткий ответ: создайте именованный класс вашего анонимного, и вместо этого протестируйте этот класс!
Длинный ответ, давайте разозлим почему!
Вы создаете анонимный класс FilterFactory
, когда компилятор видит анонимный класс, он создает конечный и видимый класс пакета. Таким образом, анонимный класс не является макетным по стандартным средним значениям, то есть через Mockito.
OK, теперь предположите, что вы хотите быть в состоянии издеваться над этим анонимный класс через Powermock. Текущие компиляторы компилируют анонимный класс со следующей схемой:
Declaring class + $ + <order of declaration starting with 1>
Mocking анонимный класс возможен, но хрупкий (и я имею в виду это). Предположим, что анонимный класс является одиннадцатым, который будет объявлен, он появится как
InputHelper$11.class
Итак, вы могли бы подготовиться к тестированию анонимного класса:
@RunWith(PowerMockRunner.class)
@PrepareForTest({InputHelper$11.class})
public class InputHelperTest {
@Test
public void anonymous_class_mocking works() throws Throwable {
PowerMockito.spy(InputHelper.BZIP2_FACTORY); // This line fails
}
}
Этот код будет компилироваться, НО в конечном итоге будет сообщен как ошибка с вашей IDE. Вероятно, IDE не знает о InputHelper$11.class
. IntelliJ, который не использует скомпилированный класс для проверки отчета о коде.
Также тот факт, что анонимное имя класса фактически зависит от порядка объявления, является проблемой, когда кто-то добавляет еще один анонимный класс раньше, нумерация может измениться. Анонимные классы сделаны для того, чтобы оставаться анонимными, что, если ребята-компиляторы однажды решат использовать буквы или даже случайные идентификаторы!
Так насмешливые анонимные классы через Powermock возможны, но хрупкие, никогда не делайте этого в real project!
EDITED ПРИМЕЧАНИЕ: Компилятор Eclipse имеет другую схему нумерации, он всегда использует 3-значное число:
Declaring class + $ + <pad with 0> + <order of declaration starting with 1>
Также я не думаю, что JLS четко укажет как компиляторы должны назвать анонимные классы.
PowerMockito.spy(InputHelper.BZIP2_FACTORY); // This line fails
whenNew(BufferedInputStream.class).withArguments(in).thenReturn(buffer);
whenNew(CBZip2InputStream.class).withArguments(buffer).thenReturn(expected);
InputStream observed = InputHelper.BZIP2_FACTORY.makeFilter(in);
PowerMockito.spy
возвращает шпиона, он не меняет значение из InputHelper.BZIP2_FACTORY
. Таким образом, вам нужно будет установить через отражение это поле. Вы можете использовать утилиту Whitebox
, которую предоставляет Powermock.
Слишком много проблем, чтобы просто проверить с помощью mocks, что анонимный фильтр использует BufferedInputStream
.
Я бы скорее написал следующий код:
Помощник ввода, который будет использовать именованный класс, я не использую имя интерфейса, чтобы сообщить пользователю, что
public class InputHelper {
public static final BufferedBZIP2FilterFactory BZIP2_FACTORY = new BufferedBZIP2FilterFactory();
}
И теперь сам фильтр:
public class BufferedBZIP2FilterFactory {
public InputStream makeFilter(InputStream in) {
BufferedInputStream buffer = new BufferedInputStream(in);
return new CBZip2InputStream(buffer);
}
}
Теперь вы можете написать тест следующим образом:
@RunWith(PowerMockRunner.class)
public class BufferedBZIP2FilterFactoryTest {
@Test
@PrepareForTest({BufferedBZIP2FilterFactory.class})
public void wraps_InputStream_in_BufferedInputStream() throws Exception {
whenNew(CBZip2InputStream.class).withArguments(isA(BufferedInputStream.class))
.thenReturn(Mockito.mock(CBZip2InputStream.class));
new BufferedBZIP2FilterFactory().makeFilter(anInputStream());
verifyNew(CBZip2InputStream.class).withArguments(isA(BufferedInputStream.class));
}
private ByteArrayInputStream anInputStream() {
return new ByteArrayInputStream(new byte[10]);
}
}
Но в конечном итоге это может привести к тому, что для этого тестового сценария не удастся использовать материал powermock, если вы вынудите CBZip2InputStream
принять только BufferedInputStream
. Обычно использование Powermock означает, что с дизайном что-то не так. По моему мнению, Powermock отлично подходит для устаревших программных продуктов, но может скрывать разработчиков при разработке нового кода; поскольку они упускают смысл хорошей части ООП, я бы даже сказал, что они разрабатывают устаревший код.
Надеюсь, что это поможет!
Я столкнулся с аналогичной проблемой с модульными тестами (особенно в шутке, когда модульные тесты запускаются локально для создания моментальных снимков, а затем сервер CI запускает (потенциально) другой часовой пояс, что приводит к сбою сравнения моментальных снимков). Я издевался над нашими Date
и некоторыми вспомогательными методами:
describe('...', () => {
let originalDate;
beforeEach(() => {
originalDate = Date;
Date = jest.fn(
(d) => {
let newD;
if (d) {
newD = (new originalDate(d));
} else {
newD = (new originalDate('2017-05-29T10:00:00z'));
}
newD.toLocaleString = () => {
return (new originalDate(newD.valueOf())).toLocaleString("en-US", {timeZone: "America/New_York"});
};
newD.toLocaleDateString = () => {
return (new originalDate(newD.valueOf())).toLocaleDateString("en-US", {timeZone: "America/New_York"});
};
newD.toLocaleTimeString = () => {
return (new originalDate(newD.valueOf())).toLocaleTimeString("en-US", {timeZone: "America/New_York"});
};
return newD;
}
);
Date.now = () => { return (Date()); };
});
afterEach(() => {
Date = originalDate;
});
});
Вы можете указать смещение часового пояса на new Date()
, например:
new Date('Feb 28 2013 19:00:00 EST')
или
new Date('Feb 28 2013 19:00:00 GMT-0500')
Поскольку Date
сохраняет время UTC (т. е. getTime
возвращается в UTC), javascript преобразует время в UTC, и когда вы вызываете такие вещи, как toString
, javascript преобразует время UTC в локальный часовой пояс браузера и возвращает строку в локальном часовом поясе, то есть, если я использую UTC+8
:
> new Date('Feb 28 2013 19:00:00 GMT-0500').toString()
< "Fri Mar 01 2013 08:00:00 GMT+0800 (CST)"
Также вы можете использовать обычный метод getHours/Minute/Second
:
> new Date('Feb 28 2013 19:00:00 GMT-0500').getHours()
< 8
(Этот параметр 8
означает, что после преобразования времени в мое местное время - UTC+8
, число часов - 8
.)
Попробуйте: date-from-timezone , он разрешает ожидаемую дату с помощью изначально доступных Intl.DateTimeFormat
.
Я использовал этот метод в одном из моих проектов в течение нескольких лет уже, но теперь я решил опубликовать его как небольшой проект ОС:)
Объект JavaScript Date
JavaScript отслеживает время в UTC внутренне, но обычно принимает вход и выход в локальное время компьютера, на котором он запущен. У этого нет никаких средств для работы со временем в других часовых поясах. Вы можете анализировать и выводить даты, которые являются UTC или Local, но вы не можете напрямую работать с другими часовыми поясами.
Чтобы быть абсолютно точным, внутреннее представление объекта Date
- это единственное число, представляя количество миллисекунд, прошедших с момента 1970-01-01 00:00:00 UTC
, независимо от секунд прыжка. В объекте Date нет часового пояса или строкового формата. Когда используются различные функции объекта Date
, локальный часовой пояс компьютера применяется к внутреннему представлению. Если функция выдает строку, тогда локальная информация компьютера может быть принята во внимание, чтобы определить, как создать эту строку. Информация зависит от каждой функции, а некоторые из них специфичны для реализации.
К счастью, есть библиотеки, которые могут это сделать. Они реализуют стандартную базу данных о часовых поясах Olson / IANA в JavaScript. У некоторых есть накладные расходы, если вы работаете в веб-браузере, так как база данных может быть немного большой, если вы хотите все это. К счастью, многие из этих библиотек позволяют выборочно выбирать зоны, которые вы хотите поддерживать, делая размер данных более приемлемым. Некоторые из них используют современные функции для получения данных о часовых поясах из API Intl
, а не для их отправки.
Для этого есть несколько библиотек:
Luxon, вероятно, является самой безопасной ставкой для всего современного использования и является самым легким весом, поскольку он использует API Intl
для его данные о часовом поясе.
Момент-часовой пояс является расширением moment.js и приводит к его собственным данным часового пояса.
js-joda - это JavaScript реализация Joda-Time API (с Java) и включает поддержку часового пояса через отдельный модуль.
BigEasy / TimeZone также находится на правильном пути.
WallTime- js достиг конца жизни , а владельцы мигрируют в момент-время.
TimeZoneJS был вокруг самого длинного, но, как известно, имеют некоторые давние ошибки, особенно при переходах на летнее время. Надеюсь, они будут исправлены в какой-то момент в будущем.
tz.js также существует некоторое время, но не очень хорошо документировано, IMHO.
Вы должны оценить эти библиотеки, чтобы увидеть, что будет отвечать вашим потребностям. Если вы не уверены, перейдите с моментом / моментом-временем.
Если вы можете ограничить свое использование в современных веб-браузерах, теперь вы можете сделать следующее без каких-либо специальных Библиотеки:
new Date().toLocaleString("en-US", {timeZone: "America/New_York"})
Это не комплексное решение, но оно работает для многих сценариев, которые требуют только преобразования вывода (от UTC или местного времени до определенного часового пояса, но не в другом направлении). Это часть API интернационализации ECMAScript (ECMA-402). Подробнее см. В этой записи . Эта таблица совместимости отслеживает, какие версии поддерживаются. Это API Intl
, упомянутый выше, что некоторые библиотеки теперь используют внутренне.
var date_time = new Date()
, значение date_time
(для меня в AEST) Date {Sun Aug 21 2016 22:18:47 GMT+1000 (AEST)}
, и поэтому кажется, что имеет i> хранит часовой пояс. Как я могу обеспечить date_time
чисто время UTC без включения часового пояса?
– user1063287
21 August 2016 в 12:22
Столкнулся с той же проблемой, использовал этот
Console.log (Date.parse («13 июня 2018 10:50:39 GMT + 1»));
Он вернет миллисекунды, к которым вы можете проверить, есть +100 timzone intialize британское время Надеюсь, что это поможет !!
Я знаю его 3 года слишком поздно, но, может быть, он может помочь кому-то другому, потому что я нашел что-то подобное, кроме библиотеки времени и времени, что не совсем то же самое, что и просьбы здесь.
ive сделал что-то подобное для немецкого часового пояса, это немного сложно из-за летнего времени и високосных лет, когда у вас есть 366 дней.
может потребоваться небольшая работа с функцией isDaylightSavingTimeInGermany, разные временные интервалы изменяются в разное время перехода на летнее время.
в любом случае, посмотрите эту страницу: https://github.com/zerkotin/german-timezone-converter/wiki
основными методами являются: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone
ive прилагает усилия к документированию, поэтому он не будет настолько запутанным.