[英]Spring value injection in mockito
我正在嘗試為以下方法編寫測試類
public class CustomServiceImpl implements CustomService {
@Value("#{myProp['custom.url']}")
private String url;
@Autowire
private DataService dataService;
我在類中的方法之一中使用注入的 url 值。 為了測試這個,我寫了一個junit類
@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })
public CustomServiceTest{
private CustomService customService;
@Mock
private DataService dataService;
@Before
public void setup() {
customService = new CustomServiceImpl();
Setter.set(customService, "dataService", dataService);
}
...
}
public class Setter {
public static void set(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
在 applicationContext-test.xml 我正在使用加載屬性文件
<util:properties id="myProp" location="myProp.properties"/>
但是在運行測試時, url 值沒有加載到 CustomService 中。 我想知道是否有辦法完成這項工作。
謝謝
import org.springframework.test.util.ReflectionTestUtils;
@RunWith(MockitoJUnitRunner.class)
public CustomServiceTest{
@InjectMocks
private CustomServiceImpl customService;
@Mock
private DataService dataService;
@Before
public void setup() {
ReflectionTestUtils.setField(customService, "url", "http://someurl");
}
...
}
我同意@skaffman 的評論。
除了您的測試使用MockitoJUnitRunner
,因此它不會尋找任何 Spring 的東西,這唯一的目的是初始化 Mockito 模擬。 ContextConfiguration
不足以用 spring 連接東西。 從技術上講,如果您想要與彈簧相關的東西,您可以使用 JUnit 的以下運行程序: SpringJUnit4ClassRunner
。
此外,在編寫單元測試時,您可能需要重新考慮使用 spring。 在單元測試中使用彈簧接線是錯誤的。 但是,如果您要編寫集成測試,那么為什么要在那里使用 Mockito,這沒有意義(如 skaffman 所說)!
編輯:現在在你的代碼中你直接在你的 before 塊中設置CustomerServiceImpl
,這也沒有意義。 那里根本不涉及春天!
@Before
public void setup() {
customService = new CustomServiceImpl();
Setter.set(customService, "dataService", dataService);
}
編輯 2:如果你想編寫CustomerServiceImpl
的單元測試,那么避免使用 Spring 的東西並直接注入屬性的值。 您也可以使用 Mockito 將DataService
模擬直接注入到測試實例中。
@RunWith(MockitoJUnitRunner.class)
public CustomServiceImplTest{
@InjectMocks private CustomServiceImpl customServiceImpl;
@Mock private DataService dataService;
@Before void inject_url() { customServiceImpl.url = "http://..."; }
@Test public void customerService_should_delegate_to_dataService() { ... }
}
您可能已經注意到,我正在使用對url
字段的直接訪問,該字段可以是包可見的。 這是一種實際注入 URL 值的測試解決方法,因為 Mockito 僅注入模擬。
您可以自動連接到一個 mutator(setter),而不僅僅是注釋私有字段。 然后你也可以從你的測試類中使用那個 setter。 無需公開,包私有即可,因為 Spring 仍然可以訪問它,否則只有您的測試可以進入(或同一包中的其他代碼)。
@Value("#{myProp['custom.url']}")
String setUrl( final String url ) {
this.url = url;
}
我不喜歡僅僅為了測試而以不同的方式自動裝配(與我的代碼庫相比),但是從測試中更改被測類的替代方法簡直是邪惡的。
我有一個從屬性文件中讀取的字符串列表。 @Before 塊中使用的 ReflectionTestUtils 類 setField 方法幫助我在測試執行之前設置了這些值。 它甚至適用於我的依賴於 Common DaoSupport 類的 dao 層。
@Before
public void setList() {
List<String> mockedList = new ArrayList<>();
mockedSimList.add("CMS");
mockedSimList.add("SDP");
ReflectionTestUtils.setField(mockedController, "ActualListInController",
mockedList);
}
你不應該嘲笑你試圖測試的東西。 這是毫無意義的,因為您不會觸及您嘗試測試的任何代碼。 而是從上下文中獲取CustomerServiceImpl
的實例。
您可以使用這個小實用程序類 ( gist ) 將字段值自動注入目標類:
public class ValueInjectionUtils {
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
private static final ConversionService CONVERSION_SERVICE = new DefaultConversionService();
private static final PropertyPlaceholderHelper PROPERTY_PLACEHOLDER_HELPER =
new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX, SystemPropertyUtils.PLACEHOLDER_SUFFIX,
SystemPropertyUtils.VALUE_SEPARATOR, true);
public static void injectFieldValues(Object testClassInstance, Properties properties) {
for (Field field : FieldUtils.getFieldsListWithAnnotation(testClassInstance.getClass(), Value.class)) {
String value = field.getAnnotation(Value.class).value();
if (value != null) {
try {
Object resolvedValue = resolveValue(value, properties);
FieldUtils.writeField(field, testClassInstance, CONVERSION_SERVICE.convert(resolvedValue, field.getType()),
true);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
}
private static Object resolveValue(String value, Properties properties) {
String replacedPlaceholderString = PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(value, properties);
return evaluateSpEL(replacedPlaceholderString, properties);
}
private static Object evaluateSpEL(String value, Properties properties) {
Expression expression = EXPRESSION_PARSER.parseExpression(value, new TemplateParserContext());
EvaluationContext context =
SimpleEvaluationContext.forPropertyAccessors(new MapAccessor()).withRootObject(properties).build();
return expression.getValue(context);
}
}
它使用org.apache.commons.lang3.reflect.FieldUtils
訪問所有用@Value
注釋的字段,然后使用 Spring 實用程序類來解析所有占位符值。 您還可以更改參數的類型properties
,以PlaceholderResolver
你喜歡用自己的PlaceholderResolver情況。 在您的測試中,您可以使用它來注入一組作為Map
或Properties
實例給出的值,如下例所示:
HashMap<String, Object> props = new HashMap<>();
props.put("custom.url", "http://some.url");
Properties properties = new Properties();
properties.put("myProp", props);
ValueInjectionUtils.injectFieldValues(testTarget, properties);
這將嘗試解析dataService
所有@Value
注釋字段。 我個人更喜歡這個解決方案而不是ReflectionTestUtils.setField(dataService, "field", "value");
因為您不必依賴硬編碼的字段名稱。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.