次のメソッドのテストクラスを記述しようとしています
public class CustomServiceImpl implements CustomService {
@Value("#{myProp['custom.url']}")
private String url;
@Autowire
private DataService dataService;
クラスのメソッドの1つで注入された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に読み込まれません。とにかくこれを成し遂げる方法があるのかと思っていました。
ありがとう
プライベートフィールドに注釈を付けるだけでなく、ミューテーター(セッター)に自動配線できます。その後、テストクラスからそのセッターを使用することもできます。それを公開する必要はありません。Springが引き続きアクセスできるので、パッケージを非公開にすることができます。
@Value("#{myProp['custom.url']}")
String setUrl( final String url ) {
this.url = url;
}
私はテストのためだけに(私のコードベースと比較して)異なるオートワイヤリングのファンではありません。
import org.springframework.test.util.ReflectionTestUtils;
@RunWith(MockitoJUnitRunner.class)
public CustomServiceTest{
@InjectMock
private CustomServiceImpl customService;
@Mock
private DataService dataService;
@Before
public void setup() {
ReflectionTestUtils.setField(customService, "url", "http://someurl");
}
...
}
@skaffmanのコメントに同意します。
テストではMockitoJUnitRunner
を使用しているため、Springのものは検索されません。この目的はMockitoモックを初期化することだけです。 ContextConfiguration
は、物を春に配線するには十分ではありません。技術的には、JUnitを使用して、スプリング関連のものが必要な場合は次のランナーを使用できます:SpringJUnit4ClassRunner
。
また、nit Testを記述しているときに、Springの使用を再検討することをお勧めします。単体テストでのスプリング配線の使用は間違っています。ただし、代わりにIntegration Testを記述しているのに、なぜ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 customService;
@Mock private DataService dataService;
@Before void inject_url() { customerServiceImpl.url = "http://..."; }
@Test public void customerService_should_delegate_to_dataService() { ... }
}
url
フィールドへの直接アクセスを使用していることに気付いたかもしれませんが、フィールドはパッケージから見えるようにすることができます。 Mockitoはモックのみを挿入するため、これは実際に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ユーティリティクラスを使用してすべてのプレースホルダー値を解決します。独自のPlaceholderResolverを使用する場合は、パラメータのタイプproperties
を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");
よりもこのソリューションを個人的に好みます。