значение messageRepository.getMessage(Locale.getDefault()) => "Здравствуй, %s!" MessageRepository messageRepository = mock(MessageRepository.class); // when messageRepository.getMessage(Locale.getDefault()); thenReturn("Здравствуй, %s!"); Всего лишь хотим сказать… Хм… может, можно «записать» вызов метода, а потом его «воспроизвести»? 12/55
new InvocationHandler() {…}); public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; } Все вызовы направляются сюда 13/55
GreetingService greetingService = new GreetingServiceImpl(messageRepository); assertEquals(greetingService.sayHello("Мир"), "Здравствуй, Мир!"); Можно еще выразительнее 19/55
реализовать лишь важные в данном тесте аспекты поведения моделируемой системы Mock-фреймворк – библиотека, упрощающая создание и использование mock-объектов, позволяет программировать их поведение в виде лаконичного DSL Есть полноценные mock-фреймворки: Mockito, JMock, EasyMock Получился примитивный mock-фреймворк 22/55
языковой конструкции для понятия «вызов метода», но, используя динамическое проксирование, можно его выразить, то есть через синтаксис языка ввести несвойственную ему семантику 23/55
completion Refactoring friendly Person person = root(Person.class); … = $(person.getDocument().getNumber()); Ошибки отлавливаются на этапе компиляции 25/55
Аллоцировать объект без вызова конструктора в Java нельзя, но если очень хочется, то можно: sun.misc.Unsafe.allocateInstance(Class) – интринсик, который это умеет Objenesis – небольшая библиотечка, которая это умеет 27/55
PersonArray.class.getMethod("get", new Class[]{int.class}); getMethod.getReturnType() // => class java.lang.Object getMethod.getGenericReturnType() // => E Type genericSuperclass = PersonArray.class.getGenericSuperclass(); ((ParameterizedType) genericSuperclass).getActualTypeArguments() // => [class ...Person] Type erasure! Формальный параметр типа Фактический параметр типа 28/55
анализа (микро)тестов (микро)производительности От разработчиков OpenJDK – для разработчиков OpenJDK (и не только) Алексей Шипилёв (The Art of) (Java) Benchmarking Gentle Introduction in JMH 35/55
void putInt(java.lang.Object o, long l, int i); public native java.lang.Object getObject(java.lang.Object o, long l); public native void putObject(java.lang.Object o, long l, java.lang.Object o1); // etc. Это не «натив», это «интринсик» (intrinsic) – метод, реализация которого будет подставлена JIT-компилятором 38/55
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafeField.setAccessible(true); final Unsafe unsafe = (Unsafe) theUnsafeField.get(null); // получаем смещение поля внутри объекта final long offset = unsafe.objectFieldOffset(field); // используем return unsafe.getObject(target, offset); 39/55
собрать из байт-кода следующую реализацию: public class CodeGenFieldAccessor implements FieldAccessor { @Override public Object get(Object target) { return ((Bean) target).name; } } Имея конкретный field, получить из него: тип target – Bean имя поля – ‘name’ Используем ASM 40/55
– private и JVM это проверит Наследуемся от sun.reflect.MagicAccessorImpl public class CodeGenFieldAccessor extends sun.reflect.MagicAccessorImpl implements FieldAccessor { @Override public Object get(Object target) { return ((Bean) target).name; } } 43/55
и непроверяемые (RuntimeException) Но это разделение существует только на уровне языка: про него знает Java-компилятор, но ничего не знает JVM Остап знал, по крайней мере, четыре почти законных способа выбросить проверяемое исключение там, где этого делать нельзя… 48/55