Hatena::ブログ(Diary)

<s>gnarl,</s>技術メモ”’<marquee><textarea>¥ このページをアンテナに追加 RSSフィード Twitter

2009-09-03

Java,JUnit,assertThrows

書いた。

説明

JUnit用のアサーションメソッド。例外表明を1行で書ける。

try {
	buckets.get(1000);
	fail();
} catch (IndexOutOfBoundsException e) {
}

これが

assertThrows(IndexOutOfBoundsException.class, buckets, "get", 1000);

こうなる。

スタティックメソッドにも対応。

// BinaryUtils.decodeInt(ba(0,1,2))
assertThrows(
	IllegalArgumentException.class,
	BinaryUtils.class,
	"decodeInt",
	ba(0, 1, 2));

ソース

パラメータの型からメソッドを解決する処理は自前で書いた。

assertThrowsに渡すときボクシングが発生するのでメソッドの型を推測するときちょっと面倒、あとこの実装だとf(int)とf(Integer)の区別が怪しいっていうかf(Object)とf(String)すら怪しいわけですがぼくのユースケースではうまく動いてるので何の問題もないですね。

メソッド解決を本気でやろうとすると結構面倒なわけだがjavaライブラリでそういう処理提供されてないんですかね。

public class TestHelper {
	public static void assertThrows(Class<? extends Throwable> expected,
			Class<?> targetClass, String methodName, Object... args)
			throws Exception {
		assertThrows(expected, null, findMethod(
			targetClass,
			methodName,
			getTypeArray(args)), args);

	}

	public static void assertThrows(Class<? extends Throwable> expected,
			Object obj, String methodName, Object... args) throws Exception {
		assertThrows(expected, obj, findMethod(
			obj.getClass(),
			methodName,
			getTypeArray(args)), args);

	}

	private static void assertThrows(Class<? extends Throwable> expected,
			Object target, Method method, Object[] args) throws Exception {
		try {
			method.invoke(target, args);
		} catch (Exception e) {
			if (e instanceof InvocationTargetException) {
				final Throwable actual =
					((InvocationTargetException) e).getTargetException();
				if (actual.getClass() == expected)
					return; // success
			}
			throw e;
		}
	}

	private static Class<?>[] getTypeArray(Object... values) {
		final Class<?>[] paramTypes = new Class<?>[values.length];
		for (int i = 0; i < paramTypes.length; i++)
			paramTypes[i] = values[i].getClass();
		return paramTypes;
	}

	private static Method findMethod(Class<?> klass, String methodName,
			Class<?>[] argTypes) throws NoSuchMethodException {
		try {
			return klass.getMethod(methodName, argTypes);
		} catch (NoSuchMethodException noSuchMethod) {
			final Method[] methods = klass.getMethods();
			find_method: for (Method m : methods) {
				if (!m.getName().equals(methodName))
					continue find_method;
				final Class<?>[] paramTypes = m.getParameterTypes();
				if (paramTypes.length != argTypes.length)
					continue find_method;
				for (int i = 0; i < paramTypes.length; i++) {
					if (!isSameType(paramTypes[i], argTypes[i]))
						continue find_method;
				}
				return m;
			}
			throw noSuchMethod;
		}
	}

	private static boolean isSameType(Class<?> type,
			Class<?> type_maybe_boxed_or_sub) {
		// exactry same
		if (type == type_maybe_boxed_or_sub)
			return true;

		// subtype
		if (type.isAssignableFrom(type_maybe_boxed_or_sub))
			return true;

		// array
		if (type.isArray()) {
			if (type_maybe_boxed_or_sub.isArray())
				return isSameType(
					type.getComponentType(),
					type_maybe_boxed_or_sub.getComponentType());
			else
				return false;
		}

		// primitive
		if (!type.isPrimitive())
			return false;
		if (type == Byte.TYPE && type_maybe_boxed_or_sub == Byte.class)
			return true;
		if (type == Short.TYPE && type_maybe_boxed_or_sub == Short.class)
			return true;
		if (type == Integer.TYPE && type_maybe_boxed_or_sub == Integer.class)
			return true;
		if (type == Long.TYPE && type_maybe_boxed_or_sub == Long.class)
			return true;

		if (type == Boolean.TYPE && type_maybe_boxed_or_sub == Boolean.class)
			return true;

		if (type == Character.TYPE
			&& type_maybe_boxed_or_sub == Character.class)
			return true;

		if (type == Float.TYPE && type_maybe_boxed_or_sub == Float.class)
			return true;
		if (type == Double.TYPE && type_maybe_boxed_or_sub == Double.class)
			return true;

		return false;
	}
}

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/gnarl/20090903/1251926667
Connection: close