Подтвердить что ты не робот

Инъекция @Полученное личное поле во время тестирования

У меня есть настройка компонента, которая по сути является лаунчером для приложения. Это настроено так:

@Component
public class MyLauncher {
    @Autowired
    MyService myService;

    //other methods
}

MyService помечен аннотацией @Service Spring и без каких-либо проблем автоматически подключается к моему классу запуска.

Я хотел бы написать несколько тестовых примеров jUnit для MyLauncher, для этого я запустил такой класс:

public class MyLauncherTest
    private MyLauncher myLauncher = new MyLauncher();

    @Test
    public void someTest() {

    }
}

Могу ли я создать объект Mock для MyService и внедрить его в myLauncher в моем тестовом классе? В настоящее время у меня нет getter или setter в myLauncher, так как Spring обрабатывает автоматическое подключение. Если возможно, я бы не хотел добавлять геттеры и сеттеры. Могу ли я сказать контрольному коду ввести фиктивный объект в переменную autowired, используя @Before инициализации @Before?

Если я говорю об этом совершенно неправильно, не стесняйтесь говорить это. Я все еще новичок в этом. Моя основная цель - просто иметь некоторый Java-код или аннотацию, которая помещает фиктивный объект в эту переменную @Autowired без необходимости писать метод установки или использовать файл applicationContext-test.xml. Я бы предпочел сохранить все для тестовых случаев в файле .java вместо того, чтобы поддерживать отдельный контент приложения только для моих тестов.

Я надеюсь использовать Mockito для макетов объектов. В прошлом я делал это с помощью org.mockito.Mockito и создавал свои объекты с помощью Mockito.mock(MyClass.class).

4b9b3361

Ответ 1

В своем тесте вы можете абсолютно инцетировать mocks на MyLauncher. Я уверен, что если вы покажете, какую издевательскую структуру вы используете, кто-то быстро даст ответ. С mockito я бы рассмотрел использование @RunWith (MockitoJUnitRunner.class) и использование аннотаций для myLauncher. Он будет выглядеть примерно так, как показано ниже.

@RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
    @InjectMocks
    private MyLauncher myLauncher = new MyLauncher();

    @Mock
    private MyService myService;

    @Test
    public void someTest() {

    }
}

Ответ 2

Принятый ответ (используйте MockitoJUnitRunner и @InjectMocks) отлично. Но если вы хотите что-то немного более легкое (без специального бегуна JUnit) и менее "волшебное" (более прозрачное), особенно для случайного использования, вы можете просто установить частные поля непосредственно с помощью интроспекции.

Если вы используете Spring, у вас уже есть класс утилиты для этого: org.springframework.test.util.ReflectionTestUtils

Использование довольно просто:

ReflectionTestUtils.setField(myLauncher, "myService", myService);

Первым аргументом является ваша цель bean, вторая - имя (обычно закрытого) поля, а последнее - значение для ввода.

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

public static void setPrivateField(Object target, String fieldName, Object value){
        try{
            Field privateField = target.getClass().getDeclaredField(fieldName);
            privateField.setAccessible(true);
            privateField.set(target, value);
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }

Ответ 3

Иногда вы можете реорганизовать свой @Component, чтобы использовать инсталляцию на основе конструктора или сеттера, чтобы настроить ваш тестовый файл (вы можете и по-прежнему полагаться на @Autowired). Теперь вы можете полностью создать свой тест без издевательской структуры, заменив тестовые заглушки (например, Martin Fowler MailServiceStub):

@Component
public class MyLauncher {

    private MyService myService;

    @Autowired
    MyLauncher(MyService myService) {
        this.myService = myService;
    }

    // other methods
}

public class MyServiceStub implements MyService {
    // ...
}

public class MyLauncherTest
    private MyLauncher myLauncher;
    private MyServiceStub myServiceStub;

    @Before
    public void setUp() {
        myServiceStub = new MyServiceStub();
        myLauncher = new MyLauncher(myServiceStub);
    }

    @Test
    public void someTest() {

    }
}

Этот метод особенно полезен, если тест и тестируемый класс находятся в одном пакете, потому что тогда вы можете использовать значение по умолчанию, package-private, чтобы другие классы не обращались к нему. Обратите внимание, что вы все еще можете иметь свой производственный код в src/main/java, но ваши тесты в каталогах src/main/test.


Если вам нравится Mockito, тогда вы оцените MockitoJUnitRunner. Это позволяет вам делать "магические" вещи, такие как @Manuel показал вам:

@RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
    @InjectMocks
    private MyLauncher myLauncher; // no need to call the constructor

    @Mock
    private MyService myService;

    @Test
    public void someTest() {

    }
}

В качестве альтернативы вы можете использовать бегун JUnit по умолчанию и вызвать MockitoAnnotations.initMocks() в методе setUp(), чтобы позволить Mockito инициализировать аннотированные значения. Вы можете найти дополнительную информацию в javadoc @InitMocks и в блоге, который я написал.

Ответ 4

Посмотрите на ссылку

Затем напишите свой тестовый пример как

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{

@Resource
private MyLauncher myLauncher ;

   @Test
   public void someTest() {
       //test code
   }
}

Ответ 5

Я полагаю, что для автоматической работы над классом MyLauncher (для myService) вам нужно разрешить Spring инициализировать его вместо вызова конструктора с помощью автоматической разметки myLauncher. Как только это происходит автоматически (и myService также получает автоматическое подключение), Spring (версии 1.4.0 и выше) предоставляет аннотацию @MockBean, которую вы можете вставить в свой тест. Это заменит совпадающие отдельные компоненты в контексте на макет этого типа. Затем вы можете дополнительно определить, что вы хотите, в методе @Before.

public class MyLauncherTest
    @MockBean
    private MyService myService;

    @Autowired
    private MyLauncher myLauncher;

    @Before
    private void setupMockBean() {
        doNothing().when(myService).someVoidMethod();
        doReturn("Some Value").when(myService).someStringMethod();
    }

    @Test
    public void someTest() {
        myLauncher.doSomething();
    }
}

Ваш класс MyLauncher может затем остаться неизменным, и ваш компонент MyService будет имитировать, методы которого возвращают значения, как вы определили:

@Component
public class MyLauncher {
    @Autowired
    MyService myService;

    public void doSomething() {
        myService.someVoidMethod();
        myService.someMethodThatCallsSomeStringMethod();
    }

    //other methods
}

Пара преимуществ этого перед другими упомянутыми методами состоит в том, что:

  1. Вам не нужно вручную вводить myService.
  2. Вам не нужно использовать бегун Mockito или правила.

Ответ 6

Я новый пользователь для Spring. Я нашел другое решение для этого. Использование отражения и создание общедоступных полей и назначение макетных объектов.

Это мой auth-контроллер, и у него есть некоторые личные свойства Autwired.

@RestController
public class AuthController {

    @Autowired
    private UsersDAOInterface usersDao;

    @Autowired
    private TokensDAOInterface tokensDao;

    @RequestMapping(path = "/auth/getToken", method = RequestMethod.POST)
    public @ResponseBody Object getToken(@RequestParam String username,
            @RequestParam String password) {
        User user = usersDao.getLoginUser(username, password);

        if (user == null)
            return new ErrorResult("Kullanıcıadı veya şifre hatalı");

        Token token = new Token();
        token.setTokenId("aergaerg");
        token.setUserId(1);
        token.setInsertDatetime(new Date());
        return token;
    }
}

И это мой тест Junit для AuthController. Я делаю публичные необходимые частные свойства и присваиваю им макетные объекты и рок:)

public class AuthControllerTest {

    @Test
    public void getToken() {
        try {
            UsersDAO mockUsersDao = mock(UsersDAO.class);
            TokensDAO mockTokensDao = mock(TokensDAO.class);

            User dummyUser = new User();
            dummyUser.setId(10);
            dummyUser.setUsername("nixarsoft");
            dummyUser.setTopId(0);

            when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) //
                    .thenReturn(dummyUser);

            AuthController ctrl = new AuthController();

            Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao");
            usersDaoField.setAccessible(true);
            usersDaoField.set(ctrl, mockUsersDao);

            Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao");
            tokensDaoField.setAccessible(true);
            tokensDaoField.set(ctrl, mockTokensDao);

            Token t = (Token) ctrl.getToken("test", "aergaeg");

            Assert.assertNotNull(t);

        } catch (Exception ex) {
            System.out.println(ex);
        }
    }

}

Я не знаю преимуществ и недостатков таким образом, но это работает. У этой техники есть немного больше кода, но эти коды могут быть разделены различными методами и т.д. Есть более хорошие ответы на этот вопрос, но я хочу указать на другое решение. Извините за мой плохой английский. Имейте хороший java для всех:)

Ответ 7

Я пытался то же самое и использовал ReflectionUtils, чтобы обернуть RESTWebservice

    ReflectionTestUtils.setField(dao,"myRESTService",myRESTService);

Теперь в дао, если я пытаюсь вызвать Webservice, он не работает. Объект не является нулевым, но вызов не выполняется. Есть ли какие-либо ограничения на REST WEB-сервис?