简体   繁体   中英

Why are JUnit assert methods not generic in Java?

I am using JUnit 4.12. The assert methods are not generic in nature. For instance, assertEquals method looks like:

static public void assertEquals(Object expected, Object actual) {..}

Why is it not like?

static public <T> void assertEquals(T expected, T actual) {..}

I felt need for generic method declaration for better compile time checking and IDE auto completion.

Having a generic method like this:

<T> void assertEquals(T expected, T actual) { /* ... */ }

gives you no type safety to avoid comparing unlike types: you can pass in anything to this method, since T degenerates to its upper bound, Object :

assertEquals("string", 0);  // Compiles fine, even though they can't be equal.

Ideone demo

And nor can you use any methods on expected and actual that aren't found on Object . So, T is basically just Object .

As such, adding generics is just over-complicating the implementation.


Now, you could define a class like this:

class GenericAssert<T> {
  void assertEquals(T expected, T actual) { /* ... */ }
}

and you could use this like:

new GenericAssert<String>().assertEquals("string", 0);  // Compiler error.

because you've now placed a tighter upper bound on the acceptable parameters of assertEquals , at class level.

But this just feels a bit awkward.

You want to look assertThat and the Hamcrest matchers; as assertThat actually works with generics:

assertThat(String reason, T actual, Matcher<? super T> matcher) 

So:

assertEquals("abc", 123); 

compiles, but fails; whereas

assertThat(123, is("abc")); 

won't even compile!

And I am not even mentioning that asserThat calls are much better to read; and give much better information when they fail. You can even use them to compare maps, sets, whatever.

Long story short: there is only one assert that anybody needs - assertThat that is!

GhostCat answer works for Java 7, but it doesn't work for Java 8+

assertThat(T actual, Matcher<? super T> matcher) 

? super T ? super T in Matcher<? super T> Matcher<? super T> means that we can match object of type T with any object with such type that it's a parent of type T . Thanks to this we can write the next code:

Integer actual = 5;
Object expected = 5;
assertThat(actual, is(expected));

But it also means you can write the next code which will be compiled and fail only in runtime:

Integer actual = 5;
String expected = "5";
assertThat(actual, is(expected));

Anyway in Java 7 the next examples will not compile:

assertThat(123, is("123"));
assertThat(Collections.singletonList(1), is(Collections.emptyList()));

We have to manually specify the type:

assertThat(123, CoreMatchers.<Object>is("123"));
assertThat(Collections.singletonList(1), is(Collections.<Integer>emptyList()));

Java 8 introduced Lambda expressions and they made improvements for type inference (shortly type inference can be made using the context of method invocation) to reduce number of times when we have to manually specify generic type. Because of this the previous example will compile successfully in Java 8:

assertThat(123, is("123"));
assertThat(Collections.singletonList(1), is(Collections.emptyList()));

For Java 8+ if you want to avoid the code to be compiled just specify the type manually:

assertThat(123, CoreMatchers.<Integer>("123")); // will not compile

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM