Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I match a Class<?> against a specific Class instance in a Hamcrest Matcher?

I would like to be able to assert that the annotation value matches the expected class:

import org.junit.Test;

import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;

public final class AnnotatedClassTest {
    @Test
    public void someAnnotationIsString() {
        assertThat(
            AnnotatedClass.class.getAnnotation(SomeAnnotation.class).value(),
            is(equalTo(String.class));
    }
}

However, this is a type error:

AnnotatedClassTest.java:9: error: no suitable method found for assertThat(Class<CAP#1>,Matcher<Class<String>>)
        assertThat(
        ^
    method MatcherAssert.<T#1>assertThat(T#1,Matcher<? super T#1>) is not applicable
      (actual argument Matcher<Class<String>> cannot be converted to Matcher<? super Class<CAP#1>> by method invocation conversion)
    method MatcherAssert.<T#2>assertThat(String,T#2,Matcher<? super T#2>) is not applicable
      (cannot instantiate from arguments because actual and formal argument lists differ in length)
    method MatcherAssert.assertThat(String,boolean) is not applicable
      (actual argument Class<CAP#1> cannot be converted to String by method invocation conversion)
  where T#1,T#2 are type-variables:
    T#1 extends Object declared in method <T#1>assertThat(T#1,Matcher<? super T#1>)
    T#2 extends Object declared in method <T#2>assertThat(String,T#2,Matcher<? super T#2>)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error

Here is the annotation class:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SomeAnnotation {
    Class<?> value();
}

And the class to which I apply that annotation:

@SomeAnnotation(String.class)
public final class AnnotatedClass {
}

The type error occurs because:

Class<?> value = AnnotatedClass.class.getAnnotation(SomeAnnotation.class).value();

And:

Matcher<Class<String>> classMatcher = is(equalTo(String.class));

Can't satisfy the signature I intend to target which is:

<T> void assertThat(T, Matcher<? super T>)

Which made more specific by fixing T based on the first parameter would be:

void assertThat(Class<?>, Matcher<? super Class<?>>)

I like the uniformity of assertThat and would prefer to avoid assertEquals.

Here is how to do it with assertEquals (notably does not answer my question):

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public final class AnnotatedClassTest {
    @Test
    public void someAnnotationIsString() {
        assertEquals(
            String.class,
            AnnotatedClass.class.getAnnotation(SomeAnnotation.class).value());
    }
}

How do I match a Class<?> against a specific Class instance in a Hamcrest Matcher?

Stating that this is impossible is an acceptable answer if you can provide a compelling explanation.

like image 491
Alain O'Dea Avatar asked Jun 07 '14 02:06

Alain O'Dea


People also ask

What is the Hamcrest matcher to test if a string contains another string?

Class StringContains. Tests if the argument is a string that contains a substring. Creates a matcher that matches if the examined String contains the specified String anywhere.

Which Hamcrest matcher is just like the && operator?

Explanation: The anyOf Hamcrest matcher is just like the || operator.

Is assertThat actual is equalTo expected ))) a valid Hamcrest assert statement?

assertEquals() is the method of Assert class in JUnit, assertThat() belongs to Matchers class of Hamcrest. Both methods assert the same thing; however, hamcrest matcher is more human-readable. As you see, it is like an English sentence “Assert that actual is equal to the expected value”.


1 Answers

A rather ugly way to do it is to use raw types.

// this declaration is RAW
Class expected = String.class;
assertThat(AnnotatedClass.class.getAnnotation(SomeAnnotation.class)
                .value(), is(equalTo(expected)));

But a nicer way to do it is to specify a generic type argument for the invocation of equalTo(..).

Class<?> expected = String.class;
assertThat(AnnotatedClass.class.getAnnotation(SomeAnnotation.class)
        .value(), is(CoreMatchers.<Class<?>>equalTo(expected)));

(You don't need to extract String.class into a variable.)

like image 182
Sotirios Delimanolis Avatar answered Oct 13 '22 22:10

Sotirios Delimanolis