2

Я пытаюсь создать издевательскую структуру в java, которая соответствует конкретному требованию проекта.Замена вызова метода java из поля вызовом метода

Сценарий, у меня есть метод

public String returnRandom(){ 

    String randomString = this.randomGenerator.returnRandom() 

    } 

randomGenerator является зависимость этого класса и вводится в объект только во время выполнения. Означает, что это будет null, если объект создан без рамки внедрения зависимостей.

Во время испытания изоляции, я хочу, чтобы заменить задание на заявление

this.randomGenerator.returnRandom(); 

с помощью метода, который возвращает бездомную случайное значение, скажем, «HelloWorld».

Я пытался использовать javassist.expr.FieldAccess для того же самого, используя который я могу заменить поле без операции и вызов метода можно изменить с помощью javassist.expr.MethodCall.

Я не могу понять, как заменить поле манекеном или нет операции. Возможно ли это с помощью java assist или я должен использовать для управления более низким уровнем байт-кода, например asm?

Примечание: Я мог бы заменить замену вызова метода, который не возникает в поле, используя javassist.expr.MethodCall. Например, если в приведенном выше примере является

public String returnRandom(){ 

    String randomString = returnRandom(); 

    } 

Я могу заменить, как

public String returnRandom(){ 

    String randomString = MockedString.getSampleRandom(); 

    } 
+0

Есть два вопроса, которые приходят, когда насмешливые, 1), описывающее поведение фиктивных объектов, 2) впрыскивать фиктивные объекты. (1) по своей сути не является тощим, потому что у вас могут быть разные типы поведения, которые необходимо протестировать. Хорошая модульная тестовая установка имеет хорошие значения по умолчанию для макетных объектов и вводит их. Единичные тесты дальше вниз могут получить доступ к этим макетным объектам, чтобы изменить их поведение. Вы хотите внедрить ложные объекты и уже удовлетворены вашим системным методом создания и описания макетных объектов? –

+0

Точно, наша целевая системная архитектура и процесс выравниваются таким образом, что для большинства объектов мы знаем их значения по умолчанию (активность, выполняемая на самой стадии проектирования). Тогда задача состоит в том, чтобы определить тесты. Мы пытаемся упростить определение теста с закрытой структурой, а не с очень открытыми тестовыми примерами стиля junit и эмпирическими механизмами, такими как все пары. И, выполняя эти определения тестов, мы пытались манипулировать байтовым кодом (или исходным кодом) для ввода этих макетных объектов. – arunvg

+0

Чтобы управлять байтовым кодом, это можно сделать во время выполнения? У меня создалось впечатление, что это невозможно. Это было бы опасно, потому что, если есть сотни тестов, перекомпиляция для каждого теста может привести к длительному циклу тестирования. –

ответ

1

Я могу решить проблему, используя javassist.expr.MethodCall. Ниже приведен класс редактора выражений, используемый для проверки выполнимости.

Это заменяет вызов targetMethod (methodcalltoReplace) с кодом, используемым для получения макетного объекта.

new ExprEditor() { 
     @Override 
     public void edit(MethodCall m) throws CannotCompileException { 
      try { 
       if (m.where().getName().equals(sourceMethod)) { 
        if (m.getMethod().getName().equals(methodcalltoReplace)) { 
         if(lineNumberOfMethodcalltoReplace == m.getLineNumber()){ 
          // The content of the hardcoded string can be replaced with runtime data 
          m.replace("$_ = ($r)"+"new com.nuwaza.aqua.sample.SampleForMethodInvocationFieldAccess().helloworld();"); 
         } 
        } 
       } 
      } catch (NotFoundException e) { 
       e.printStackTrace(); 
      } 
      super.edit(m); 
     } 

Для документации подробно см, Javaassist tutorial, introspection and customization

3

Самое лучшее, что вы можете сделать, это создать интерфейс для генератора случайных чисел, скажем:

public interface RandomGenerator { 
    public String returnRandom(); 
} 

Тогда ваш оригинальный генератор случайных данных может реализовать этот интерфейс, а класс, который использует случайный генератор, может зависеть от класса с интерфейсом RandomGenerator.

Как только у вас есть это, это довольно просто проверить. Вы создаете генератор макетов, который делает то, что вы хотите:

public class MockRndGenerator implements RandomGenerator { 
    public String returnRandom() { 
    return "Helloworld"; 
    } 
} 

и когда вы тестируете, вы вводите этот класс вместо оригинала.

public class Demo { 
    public Demo (RandomGenerator rndGenerator) { 
    this.randomGenerator = rndGenerator; 
    } 
    public String returnRandom(){ 
    String randomString = this.randomGenerator.returnRandom() 
    } 
} 

* UPDATE *

Поскольку я не могу добавить код в комментариях, вот решение Mockito:

вы всегда можете использовать Mockito, чтобы избежать создания физических издевается, и то вы можете настроить ожидания и проверки на пути

class Test { 
    public static rndTest() { 
    RandomGenerator rnd = Mockito.mock(RandomGenerator.class); 
    Mockito.when(rnd.returnRandom()).thenReturn("Helloworld"); 
    Demo = new Demo(rnd); 
    } 
} 
+0

Спасибо, меза за ответ. Это именно та проблема, с которой мы пытаемся управлять с помощью новой структуры тестирования. Мы использовали этот подход раньше и привели к большому количеству классов java для макета. Мы были обеспокоены ремонтопригодностью тестов и хотели создать лучшую структуру, дающую разработчикам меньшие накладные расходы при написании и обслуживании модульных тестов. – arunvg

+0

Да, это просто и доступно для тестирования. И - что более важно - это приводит к хорошему стилю программирования. – Seelenvirtuose

+0

aurnvg: вы всегда можете использовать 'Mockito', чтобы не создавать физические макеты, а затем вы можете настроить ожидания и проверки на этом пути. класс Тест { Публичный статический rndTest() { RandomGenerator rnd = Mockito.mock (RandomGenerator.class); Mockito.when (rnd.returnRandom()). ThenReturn ("Helloworld"); Демо = новое демо (rnd); } } – meza

0

Один из подходов, который я сделал в прошлом, - создать статический метод задней двери, чтобы заменить ваш случайный генератор на насмешку.Затем вы можете использовать mockito для указания того, что он должен вернуть. Ставя его, вы избегаете создания многих из них, всего один для всего класса. Как и все остальное, у него есть плюсы и минусы, но он может соответствовать вашим конкретным потребностям.

Вместо прямого вызова поля, как и в вашем примере, вы можете использовать метод вместо getRandGen(). Этот метод будет либо использовать локальный случайный генератор, либо издеваться над статическим, если он установлен.

По существу это задняя дверь, чтобы переписать выбранный вами объект структуры зависимостей с тем, который вы выбираете во время выполнения во время модульного тестирования, и является статичным, поэтому он влияет на весь класс, а не только на конкретные экземпляры.

+0

Thankyou Jose Martinez. В основном вы предлагаете сделать доступ зависимостей от отдельного метода (в основном, для частного). Наши рамки могли бы достичь этого, выполняя наши (строгие) требования. (См. Пример в примечании, добавленном в вопрос). Но нас беспокоил аспект «юзабилити». Это означает, что разработчики должны кодировать, имея в виду, ограничение инструмента тестирования. Это последнее, что мы хотим сделать ..., чтобы вторгнуться в разработчиков, свободно писать код. – arunvg

+0

Хммм. Это может быть субъективной проблемой. Скажите, что ваше тестирование модулей разработчиков является гражданином первого класса, он сначала попадает в поезд. У всех проблемных дочерних зависимостей должна быть входная дверь для издевательства. Это решает проблему создания множества классов, потому что ваша издевательская структура может обнаруживать статические сеттеры и может вставлять макеты для разработчиков. Затем разработчики могут решить, какие из этих макетных объектов они хотят высмеять дальше. Вы даже можете писать свои классы с предлагаемыми дефолтными объектами по умолчанию, чтобы они не нуждались в разработке во время модульного тестирования. –

+1

Спасибо, Хосе Мартинес. :). Мы стремимся решать проблемы, достигая нашей цели и не ставя под угрозу ограничения игры. Для нас разработчики похожи на наших клиентов, требуя лучшего пользовательского опыта. – arunvg