Как мне высмеять поле @Value с автоповтором в Spring с помощью Mockito?
Я использую Spring 3.1.4.RELEASE и Mockito 1.9.5. В моем классе Spring у меня есть:
@Value("#{myProps['default.url']}")
private String defaultUrl;
@Value("#{myProps['default.password']}")
private String defaultrPassword;
// ...
Из моего теста JUnit, который я сейчас настроил так:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
Я хотел бы высмеять значение для моего поля defaultUrl. Обратите внимание: я не хочу издеваться над значениями для других полей - я хотел бы сохранить их такими, какие они есть, только поле "defaultUrl". Также обратите внимание, что у меня нет явных методов "setter" (например, setDefaultUrl
) в моем классе, и я не хочу создавать их только для целей тестирования.
Учитывая это, как я могу высмеять значение для этого одного поля?
Ответы
Ответ 1
Вы можете использовать магию Spring ReflectionTestUtils.setField
, чтобы не вносить какие-либо изменения в свой код.
Ознакомьтесь с этим учебником для получения дополнительной информации, хотя вам, вероятно, это не понадобится, так как метод очень прост в использовании
UPDATE
С момента введения Spring 4.2.RC1 теперь можно установить статическое поле без необходимости поставки экземпляра класса. См. эту часть документации и this commit.
Ответ 2
Это был уже третий раз, когда я гуглял себя на этот пост, так как я всегда забывал, как насмехаться над полем @Value. Хотя принятый ответ правильный, мне всегда нужно некоторое время, чтобы правильно вызвать вызов setField, так что, по крайней мере, для себя я вставлю пример фрагмента здесь:
Производственный класс:
@Value("#{myProps[‘some.default.url']}")
private String defaultUrl;
Тестовый класс:
import org.springframework.test.util.ReflectionTestUtils;
ReflectionTestUtils.setField(instanceUnderTest, "defaultUrl", "http://foo");
// Note: Don't use MyClassUnderTest.class, use the instance you are testing itself
// Note: Don't use the referenced string "#{myProps[‘some.default.url']}",
// but simply the FIELDs name ("defaultUrl")
Ответ 3
Вы также можете высмеять свою конфигурацию свойств в своем тестовом классе
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
@Configuration
public static class MockConfig{
@Bean
public Properties myProps(){
Properties properties = new Properties();
properties.setProperty("default.url", "myUrl");
properties.setProperty("property.value2", "value2");
return properties;
}
}
@Value("#{myProps['default.url']}")
private String defaultUrl;
@Test
public void testValue(){
Assert.assertEquals("myUrl", defaultUrl);
}
}
Ответ 4
Я хотел бы предложить связанное решение, которое заключается в передаче @Value
-annotated в качестве параметров конструктору вместо использования класса ReflectionTestUtils
.
Вместо этого:
public class Foo {
@Value("${foo}")
private String foo;
}
а также
public class FooTest {
@InjectMocks
private Foo foo;
@Before
public void setUp() {
ReflectionTestUtils.setField(Foo.class, "foo", "foo");
}
@Test
public void testFoo() {
// stuff
}
}
Сделай это:
public class Foo {
private String foo;
public Foo(@Value("${foo}") String foo) {
this.foo = foo;
}
}
а также
public class FooTest {
private Foo foo;
@Before
public void setUp() {
foo = new Foo("foo");
}
@Test
public void testFoo() {
// stuff
}
}
Преимущества этого подхода: 1) мы можем создать экземпляр класса Foo без контейнера зависимостей (это просто конструктор), и 2) мы не связываем наш тест с деталями нашей реализации (отражение связывает нас с именем поля с помощью строки, что может вызвать проблемы, если мы изменим имя поля).
Ответ 5
Вы можете использовать эту волшебную аннотацию Spring Test:
@TestPropertySource(properties = { "my.spring.property=20" })
смотрите org.springframework.test.context.TestPropertySource
Например, это тестовый класс:
@ContextConfiguration(classes = { MyTestClass.Config.class })
@TestPropertySource(properties = { "my.spring.property=20" })
public class MyTestClass {
public static class Config {
@Bean
MyClass getMyClass() {
return new MyClass ();
}
}
@Resource
private MyClass myClass ;
@Test
public void myTest() {
...
И это класс со свойством:
@Component
public class MyClass {
@Value("${my.spring.property}")
private int mySpringProperty;
...
Ответ 6
Я использовал приведенный ниже код, и он работал для меня:
@InjectMocks
private ClassABC classABC = new ClassABC();
@Before
public void setUp() {
ReflectionTestUtils.setField(classABC, "constantFromConfigFile", 3);
}
Ссылка: https://www.jeejava.com/mock-an-autowired-value-field-in-spring-with-junit-mockito/
Ответ 7
Также обратите внимание, что в моем классе нет явных методов "установки" (например, setDefaultUrl), и я не хочу создавать какие-либо только для целей тестирования.
Одним из способов решения этой проблемы является изменение класса для использования Constructor Injection, которое используется для тестирования и Spring инъекций. Нет больше размышлений :)
Таким образом, вы можете передать любую строку, используя конструктор:
class MySpringClass {
private final String defaultUrl;
private final String defaultrPassword;
public MySpringClass (
@Value("#{myProps['default.url']}") String defaultUrl,
@Value("#{myProps['default.password']}") String defaultrPassword) {
this.defaultUrl = defaultUrl;
this.defaultrPassword= defaultrPassword;
}
}
И в своем тесте просто используйте его:
MySpringClass MySpringClass = new MySpringClass("anyUrl", "anyPassword");