Как настроить тестирование при внедрении зависимости от суперкласса? - PullRequest
3 голосов
/ 06 января 2020

Мой код настроен так.

abstract class BaseController {
   @Inject Store store; 
}

class MyController extends BaseController {
   private final Validator validator;

   @Inject
   public MyController(Validator validator) {
      this.validator = validator;
   }

   public boolean someMethod() {
      a = store.storingMethod();
      b = validator.validate(a);
      ...
      ...
      return true;
   }
}

Теперь я хотел написать тесты для myController. В тесте я хочу использовать введенный Store, но хочу макетировать Validator. Я пробовал что-то вроде этого ). Но я хочу иметь класс Store в Base, поскольку он будет использоваться другими классами, расширяющими его.

Как настроены мои классы, Как мне ввести Store во время тестирования?

Ответы [ 3 ]

5 голосов
/ 06 января 2020

Не используйте полевую инъекцию. Используйте конструктор инъекций.

abstract class BaseController {
    final Store store; 

    BaseController(Store store) {
        this.store = store;
    }
}

class MyController extends BaseController {
   private final Validator validator;

   @Inject
   public MyController(Validator validator, Store store) {
      super(store);
      this.validator = validator;
   }
}

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

Spring @Autowire в свойствах по сравнению с конструктором

Внедрение зависимостей: Ввод полей по сравнению с инжектором Constructor?

Также стоит отмечая, что

Команда Spring обычно выступает за инжекцию конструктора

Источник

2 голосов
/ 06 января 2020

Обычно я решаю эту проблему, используя следующий шаблон:

abstract class BaseController {
    private final Store store; 

    protected BaseController (Store store) {
        this.store = store;
    }

    protected Store getStore() {
        return this.store;
    }
}

class MyController extends BaseController {
    private final Validator validator;

    @Inject
    public MyController(Store store, Validator validator) {
       super(store);
       this.validator = validator;
    }

    public boolean someMethod() {
        a = getStore().storingMethod();
        b = validator.validate(a);
        ...
        ...
        return true;
    }
}

Таким образом, базовый класс может использоваться независимо от любой доступной инфраструктуры инъекций.

1 голос
/ 06 января 2020

Вы можете использовать ReflectionTestUtils для установки значения поля.

Импортировать его в pom. xml:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.2.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.2.RELEASE</version>
    <scope>test</scope>
</dependency>

Использовать его для установки Ваш магазин:

@RunWith(MockitoJUnitRunner.class)
public class MyControllerTest() {
   private MyController myController;
   @Mock private Validator validator;

   @Before
   public void before() {
      myController = new MyController(validator);
      ReflectionTestUtils.setField(myController, "store", new YourTestStore());

      // more testing
   }
}

Подробнее об этом @ https://www.baeldung.com/spring-reflection-test-utils

Также обратите внимание, что я не думаю, что это лучшая практика.

...