I am working with a Java Web Application that has a lot of unit tests. We run the unit tests within Eclipse. I am going through the tests and refactoring some of them. I have seen a few tests that are written like this (I'll boil it down to the assertions, my literal examples actually are variables in the tests)
assertEquals(new Integer(7), new Long(7));
This test passes!, and I don't understand why, since the types are different. After seeing this behavior, I created a simple Java project in Eclipse and wrote basically the same unit test
assertEquals(new Integer(7), new Long(7);
and it Failed as I expected. I don't need any help, I was just curious how the test passes in one environment and fails (as it should) in another.
If you look at the equals method for java.lang.Long it says:
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
So new Long(7).equals(new Integer(7)) should be false, because Integer instanceof Long is false. This test program confirms that:
public class Stuff {
public static void main(String[] args) {
System.out.println("int equals long : " + new Integer(7).equals(new Long(7)));
System.out.println("long equals int : " + new Long(7).equals(new Integer(7)));
}
}
which prints out
int equals long : false
long equals int : false
I'm guessing the web application test that came up with the opposite result used an add-on like ComparableAssert, which has this signature
public static void assertEquals(java.lang.Comparable expected,
java.lang.Comparable actual)
It would be easy to mistake for the other, especially if the test uses static imports. Longs and Integers are comparable to each other (new Long(7).compareTo(new Integer(7)) evaluates to 0) so this assert would succeed.
Related
I've been learning Java just for a bit, so please advise how exception throwing test should look like in this case?
I have following Gambling Machine Class. And then 2 tests for it. I do not really know what should follow the "Integer" in second method (shouldThrowWhenNumbersOutOfRange). Could you please advise as to the exact syntax?
public class GamblingMachine {
public int howManyWins(Set<Integer> userNumbers) throws InvalidNumbersException {
validateNumbers(userNumbers);
Set<Integer> computerNumbers = generateComputerNumbers();
int count = 0;
for (Integer number : userNumbers) {
if (computerNumbers.contains(number)) {
count++;
}
}
return count;
}
private void validateNumbers(Set<Integer> numbers) throws InvalidNumbersException {
if (numbers.size() != 6) {
throw new InvalidNumbersException();
}
if (numbers.stream().anyMatch(number -> number < 1 || number > 49)) { //anyMatch-function to check whether any element in list satisfy given condition
throw new InvalidNumbersException();
}
}
private Set<Integer> generateComputerNumbers() {
Set<Integer> numbers = new HashSet<>();
Random generator = new Random();
while(numbers.size() < 6) {
numbers.add(generator.nextInt(49) + 1);
}
return numbers;
}
}
private GamblingMachine machine = new GamblingMachine();
#ParameterizedTest
#NullAndEmptySource
public void shouldThrowWhenNumbersEmpty(Set<Integer> numbers) throws InvalidNumbersException {
Assertions.assertThrows(NumberFormatException.class, () -> {
Integer.parseInt(" ");
});
}
#ParameterizedTest
#CsvFileSource(resources ="/numbersOutOfRange.cvs", numLinesToSkip = 1)
public void shouldThrowWhenNumbersOutOfRange(Set<Integer> numbers) throws InvalidNumbersException {
Assertions.assertThrows(NumberFormatException.class, () -> {
Integer. //how code should look like here?
});
}
The point of a test is to, you know, test something. Your shouldThrowWhenNumbersEmpty test doesn't do that (well, it tests that Integer.parseInt(" ") throws something. It does, of course. You... don't have to test the core libraries).
In other words, your gambling machine tests need to be calling some stuff from your GamblingMachine class. The idea is to test GamblingMachine. Not to test Integer.parseInt.
It's also a bizarre test: Why in the blazes is shouldThrowWhenNumbersEmpty parameterized? I assume the point of that test is: "Ensure that the gambling machine works as designed when passing an empty set of numbers in, specifically, the part of the design that states that an InvalidNumbersException is thrown if you do that".
Which is done with something like:
#Test
public void shouldThrowWhenNumbersEmpty() {
Assertions.assertThrows(InvalidNumbersException.class, () -> {
Set<Integer> empty = Set.of();
machine.howManyWins(empty);
});
}
Parameterized tests are a fairly exotic concept. Your test setup appears to be falling into a trap: It appears to be set up that you repeat all the logic that is already in your gamblingmachine class, to then apply this logic to the incoming (parameterized) data, figure out what your gambling machine ought to be doing, and then double check its work.
That's not how you should write tests. Tests focus on a specific result. Parameterized tests can make sense, but only if the stuff you have to do for any given input is roughly the same. For example:
Good use of parameterized testing
You have a csv file containing a bunch of lines, each of which has 6 rolls + the correct answer. Your parameterized test treats each line the same: Call howManyWins using the 6 rolls as input, then check that howManyWins returns the expected value.
Bad use of parameterized testing
You have a csv file containing a bunch of lines, each of which has 6 rolls. Your parameterized test will calculate the right result for the rolls, then invoke gambling machine, and check that the gambling machine gives the same answer as what you calculated.
This is bad: You're just repeating the code. It also means your test code is itself doing more than the very basics (it's doing a bunch of business logic), thus raising the question: Who tests your test, then?
Both of your test methods seem like they should NOT be parameterized, unless that csv also contains results.
I've read in lot of places that test methods should/must be void, but no one says what is the reason for this.
I found in MethodValidator the following check without comments/javadocs.
if (each.getReturnType() != Void.TYPE) {
errors.add(new Exception("Method " + each.getName()
+ " should be void"));
}
So why it should be void?
Ask you the reverse question : why JUnit test methods would need to be not void ?
No reason : because a test method is not designed to return a result that will be exploited by a client class.
The goal of an unit test is validating some assertions. A test runner invokes the test methods and and this is the runner that interprets any assertion failure or any exception thrown during the test execution to produce the test result.
We could wonder why tests don't return the assertion result.
But it would be a bad idea as it would be very cumbersome to write unit tests :
#Test
public AssertionResult foo(){
Bar actualBar = foo.doThat(...);
if (actualBar == null){
return AssertionResult.fail("actualBar == null");
}
}
Write something like that is really readable and straight :
#Test
public void foo(){
Bar actualBar = foo.doThat(...);
Assert.assertNotNull(actualBar);
}
We could also wonder why test methods cannot be invoked by other test methods such as :
#Test
public int foo(){
Bar actualBar = foo.doThat(...);
//...
return intValue;
}
#Test
public void fooWithComputedInt(){
Bar actualBar = foo.doThat(foo());
//...
}
But this also would be not a good idea as this would couple the tests execution while unit tests execution has to be isolated from others. And it also would make the tests be executed multiple times and unit tests have to be executed as fast as possible.
So really no value to make a test method return other thing than void.
This is purely a design choice. JUnit does not know about your code, so it could not do anything if your method would return something.
So either it should discard a return value, or require you to use "void" methods. The authors chose the latter option - you could argue that this slightly better because it's not confusing the reader.
Note that non-#Test methods are free to do whatever they want - they don't have this limitation.
A single JUnit test being run under JUnit 4.11 fail the majority of the time while being run via either to module test suite (40 runs: 2 failures, 38 passes), or the class test suite (40 runs: 6 failures, 34 passes), but running the test method by itself did not produce a single failure (50 runs: 0 failures, 50 passes).
To summarize what is happening, the equals(Object MyObject) implementation returns true if the org.joda.time.DateTime corresponding to the key Stamp.START or the key Stamp.STOP is the same for the current instance as the one in instance passed to the method. Here's the code:
import org.joda.time.DateTime;
...
private final Map<Stamp, DateTime> timeStampMap;
...
#Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (obj == null || getClass() != obj.getClass()) { return false; }
final MyObject other = (MyObject) obj;
return (Objects.equals(this.timeStampMap.get((Stamp.START)),
other.timeStampMap.get(Stamp.START))
&& Objects.equals(this.timeStampMap.get(Stamp.STOP),
this.timeStampMap.get(Stamp.STOP)));
}
...
public enum Stamp {
START,
STOP
}
And the test itself:
#Test
#Config(configuration = TestConfig.NO_CONFIG)
public void equalityTest() {
MyObject a = new MyObject(BigDecimal.TEN);
MyObject b = a;
assertThat(a.hashCode(), is(b.hashCode()));
assertTrue(a.equals(b));
b = new MyObject(BigDecimal.TEN);
// This line produces the failure
assertThat(a, is(not(b)));
}
Why would this test only fail when run under either test suite, but not when run on it's own?
Since you are using Joda time, an alternative approach might be to fix the current time to something of your choosing using DateTimeUtils.setCurrentMillisFixed(val).
For example:
#Test
#Config(configuration = TestConfig.NO_CONFIG)
public void equalityTest() {
DateTimeUtils.setCurrentMillisFixed(someValue);
MyObject a = new MyObject(BigDecimal.TEN);
MyObject b = a;
assertThat(a.hashCode(), is(b.hashCode()));
assertTrue(a.equals(b));
DateTimeUtils.setCurrentMillisFixed(someValue + someOffset);
b = new MyObject(BigDecimal.TEN);
// This line produces the failure
assertThat(a, is(not(b)));
}
I suggest making the code more testable. Instead of having the code get the date directly, you can pass in an interface named Clock:
public interface Clock {
DateTime now();
}
Then you could add Clock to the constructor:
MyObject(BigDecimal bigDecimal, Clock clock) {
timeStampMap.put(Stamp.START, clock.now());
}
For production code, you can make a helper constructor:
MyObject(BigDecimal bigDecimal) {
this(bigDecimal, new SystemClock());
}
...where SystemClock looked like this:
public class SystemClock implements Clock {
#Override
public DateTime now() {
return new DateTime();
}
}
Your tests could either mock Clock or you could create a fake clock implementation.
Over the process of trying to produce an MCVE and author the question, I discovered something interesting:
When the test is run at the method level, note the timestamp difference of 1 millisecond. The difference is never less than that:
[START: 2015-02-26T11:53:20.581-06:00, STOP: 2015-02-26T11:53:20.641-06:00, DURATION: 0.060]
[START: 2015-02-26T11:53:20.582-06:00, STOP: 2015-02-26T11:53:20.642-06:00, DURATION: 0.060]
But when I run the test ends up being run as part of the suites, this happens nearly every single time:
[START: 2015-02-26T12:25:31.183-06:00, STOP: 2015-02-26T12:25:31.243-06:00, DURATION: 0.060]
[START: 2015-02-26T12:25:31.183-06:00, STOP: 2015-02-26T12:25:31.243-06:00, DURATION: 0.060]
Zero difference. Weird right?
My best guess is that the JVM is proverbially all warmed up and has some momentum built by the time it reaches this particular test when running the test suites. So much so, that the instantiations occur so quickly as to be nearly simultaneous. The tiny amount of time that passes between the time that MyObject a is instantiated and b is assigned until b is reassigned as a new MyObject is so minute as to produce a MyObject with an identical pair of DateTimes.
Turns out, there are a few usable solutions:
The Solution I Went With:
This is really similar to Duncan's. Call DateTimeUtils.setCurrentMillisOffset(val) before reassigning MyObject b and then reset immediately afterward, since I only need the offset long enough to force a difference in the DateTimes between MyObjects a and b:
#Test
#Config(configuration = TestConfig.NO_CONFIG)
public void equalityTest() {
MyObject a = new MyObject(BigDecimal.TEN);
MyObject b = a;
assertThat(a.hashCode(), is(b.hashCode()));
assertTrue(a.equals(b));
// Force an offset
DateTimeUtils.setCurrentMillisOffset(1000);
b = new MyObject(BigDecimal.TEN);
// Clears the offset
DateTimeUtils.setCurrentMillisSystem();
assertThat(a, is(not(b)));
}
Namshubwriter's Solution (link to answer):
Easily the best solution in cases where this issue will likely be seen throughout a project and/or in actual use.
Duncan's Solution (link to answer):
Set the current time to return a fixed time by calling DateTimeUtils.setCurrentMillisFixed(val) at the beginning of the unit test, then adding an offset to that time by calling DateTimeUtils.setCurrentMillisFixed(val + someOffset) to force the difference before reassigning MyObject b. Click the link to jump right to his solution with the code.
It is worth pointing out that you'll need to call DateTimeUtils.setCurrentMillisSystem() at some point to reset the time, otherwise other tests dependent on the time could be affected.
Original Solution:
I think it is worth mentioning here that, it is my understanding this is the only solution that does not depend on the program having certain security privileges on the parent system.
Place a call to Thread.sleep() ensure that there is a time separation between the DateTime timestamps of the two MyObjects:
#Test
#Config(configuration = TestConfig.NO_CONFIG)
public void equalityTest() {
MyObject a = new MyObject(BigDecimal.TEN);
MyObject b = a;
assertThat(a.hashCode(), is(b.hashCode()));
assertTrue(a.equals(b));
try {
Thread.sleep(0, 1);
} catch (Exception e) {
e.printStackTrace();
}
b = new MyObject(BigDecimal.TEN);
// Line that was failing
assertThat(a, is(not(b)));
}
While poking around the JDK 1.7 source I noticed these methods in Boolean.java:
public static Boolean valueOf(String s) {
return toBoolean(s) ? TRUE : FALSE;
}
private static boolean toBoolean(String name) {
return ((name != null) && name.equalsIgnoreCase("true"));
}
So valueOf() internally calls toBoolean(), which is fine. I did find it interesting to read how the toBoolean() method was implemented, namely:
equalsIgnoreCase() is reversed from what I would normally do (put the string first), and then
there is a null check first. This seems redundant if point 1 was adopted; as the first/second check in that method is a null check.
So I thought I would put together a quick test and check how my implementation would work compared with the JDK one. Here it is:
public class BooleanTest {
private final String[] booleans = {"false", "true", "null"};
#Test
public void testJdkToBoolean() {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
for (String aBoolean : booleans) {
Boolean someBoolean = Boolean.valueOf(aBoolean);
}
}
long end = System.currentTimeMillis();
System.out.println("JDK Boolean Runtime is: " + (end-start));
}
#Test
public void testModifiedToBoolean() {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
for (String aBoolean : booleans) {
Boolean someBoolean = ModifiedBoolean.valueOf(aBoolean);
}
}
long end = System.currentTimeMillis();
System.out.println("ModifiedBoolean Runtime is: " + (end-start));
}
}
class ModifiedBoolean {
public static Boolean valueOf(String s) {
return toBoolean(s) ? Boolean.TRUE : Boolean.FALSE;
}
private static boolean toBoolean(String name) {
return "true".equalsIgnoreCase(name);
}
}
Here is the result:
Running com.app.BooleanTest
JDK Boolean Runtime is: 37
ModifiedBoolean Runtime is: 34
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.128 sec
So not much of a gain, especially when distributed over 1m runs. Really not all that surprising.
What I would like to understand is how these differ at the bytecode level. I am interested in delving into this area but don't have any experience. Is this more work than is worth while? Would it provide a useful learning experience? Is this something people do on a regular basis?
There would be no performance gain for a couple of reasons:
It's just not that expensive of an operation to check whether or not name == null.
The thing that takes time is loading the value of name...which has to be loaded in either case.
name==null is faster then calling String.equalsIgnoreCase since it's a simple equality test rather than a function call.
These don't matter anyway because the architecture will likely use predictive branching and thus if most of your calls aren't for null strings, the architecture will start loading the branching instructions as if your strings are not null.
First, bytecode is very close to Java source. It can't give you much more information about the performance except some special cases (e.g. compile-time expression evaluation). Much more important is JIT compilation done by the JVM.
Some background: In early Java versions, it was rather a machine-well-readable version of source code. Decompiling such early Java versions is rather straightforward. You will lose comments and code will be slightly different. The hardest work of such decompiler is probably reconstructing the loops. In today Java versions, the decompilers have to be slightly more complex, because the language has been changed (inner classes, generics, …) more than the bytecode. But the bytecode is still very close to the source, even today.
Second, the redundant null check might not be important. JVM is able to remove some unneeded checks, even the automatically generated array bounds checks if they are surely unneeded.
Third, benchmarks are very tricky and even more tricky on the JVM. JVM "warms up", so the second benchmark might benefit from some optimizations done for the first benchmark. In some cases, the opposite might also happen – some optimistic optimisation must be discarded and the second benchmark is slower. Moreover, running the code only once creates huge error in the results.
Does anybody know why JUnit 4 provides assertEquals(foo,bar) but not assertNotEqual(foo,bar) methods?
It provides assertNotSame (corresponding to assertSame) and assertFalse (corresponding to assertTrue), so it seems strange that they didn't bother including assertNotEqual.
By the way, I know that JUnit-addons provides the methods I'm looking for. I'm just asking out of curiosity.
I'd suggest you use the newer assertThat() style asserts, which can easily describe all kinds of negations and automatically build a description of what you expected and what you got if the assertion fails:
assertThat(objectUnderTest, is(not(someOtherObject)));
assertThat(objectUnderTest, not(someOtherObject));
assertThat(objectUnderTest, not(equalTo(someOtherObject)));
All three options are equivalent, choose the one you find most readable.
To use the simple names of the methods (and allow this tense syntax to work), you need these imports:
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
There is an assertNotEquals in JUnit 4.11: https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.11.md#improvements-to-assert-and-assume
import static org.junit.Assert.assertNotEquals;
I wonder same. The API of Assert is not very symmetric; for testing whether objects are the same, it provides assertSame and assertNotSame.
Of course, it is not too long to write:
assertFalse(foo.equals(bar));
With such an assertion, the only informative part of the output is unfortunately the name of the test method, so descriptive message should be formed separately:
String msg = "Expected <" + foo + "> to be unequal to <" + bar +">";
assertFalse(msg, foo.equals(bar));
That is of course so tedious, that it is better to roll your own assertNotEqual. Luckily in future it will maybe be part of the JUnit: JUnit issue 22
I'd argue that the absence of assertNotEqual is indeed an asymmetry and makes JUnit a bit less learnable. Mind that this is a neat case when adding a method would diminish the complexity of the API, at least for me: Symmetry helps ruling the bigger space.
My guess is that the reason for the omission may be that there are too few people calling for the method. Yet, I remember a time when even assertFalse did not exist; hence, I have a positive expectation that the method might eventually be added, given that it is not a difficult one; even though I acknowledge that there are numerous workarounds, even elegant ones.
I'm coming to this party pretty late but I have found that the form:
static void assertTrue(java.lang.String message, boolean condition)
can be made to work for most 'not equals' cases.
int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;
I am working on JUnit in java 8 environment, using jUnit4.12
for me: compiler was not able to find the method assertNotEquals, even when I used
import org.junit.Assert;
So I changed assertNotEquals("addb", string);toAssert.assertNotEquals("addb", string);
So if you are facing problem regarding assertNotEqual not recognized, then change it to Assert.assertNotEquals(,); it should solve your problem
The obvious reason that people wanted assertNotEquals() was to compare builtins without having to convert them to full blown objects first:
Verbose example:
....
assertThat(1, not(equalTo(Integer.valueOf(winningBidderId))));
....
vs.
assertNotEqual(1, winningBidderId);
Sadly since Eclipse doesn't include JUnit 4.11 by default you must be verbose.
Caveat I don't think the '1' needs to be wrapped in an Integer.valueOf() but since I'm newly returned from .NET don't count on my correctness.
It's better to use the Hamcrest for negative assertions rather than assertFalse as in the former the test report will show a diff for the assertion failure.
If you use assertFalse, you just get an assertion failure in the report. i.e. lost information on cause of the failure.
Usually I do this when I expect two objects to be equal:
assertTrue(obj1.equals(obj2));
and:
assertFalse(obj1.equals(obj2));
when they are expected to be unequal. I am aware that this not an answer to your question but it is the closest I can get. It could help others searching for what they can do in JUnit versions before JUnit 4.11.
I agree totally with the OP point of view. Assert.assertFalse(expected.equals(actual)) is not a natural way to express an inequality.
But I would argue that further than Assert.assertEquals(), Assert.assertNotEquals() works but is not user friendly to document what the test actually asserts and to understand/debug as the assertion fails.
So yes JUnit 4.11 and JUnit 5 provides Assert.assertNotEquals() (Assertions.assertNotEquals() in JUnit 5) but I really avoid using them.
As alternative, to assert the state of an object I general use a matcher API that digs into the object state easily, that document clearly the intention of the assertions and that is very user friendly to understand the cause of the assertion failure.
Here is an example.
Suppose I have an Animal class which I want to test the createWithNewNameAndAge() method, a method that creates a new Animal object by changing its name and its age but by keeping its favorite food.
Suppose I use Assert.assertNotEquals() to assert that the original and the new objects are different.
Here is the Animal class with a flawed implementation of createWithNewNameAndAge() :
public class Animal {
private String name;
private int age;
private String favoriteFood;
public Animal(String name, int age, String favoriteFood) {
this.name = name;
this.age = age;
this.favoriteFood = favoriteFood;
}
// Flawed implementation : use this.name and this.age to create the
// new Animal instead of using the name and age parameters
public Animal createWithNewNameAndAge(String name, int age) {
return new Animal(this.name, this.age, this.favoriteFood);
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getFavoriteFood() {
return favoriteFood;
}
#Override
public String toString() {
return "Animal [name=" + name + ", age=" + age + ", favoriteFood=" + favoriteFood + "]";
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((favoriteFood == null) ? 0 : favoriteFood.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Animal)) return false;
Animal other = (Animal) obj;
return age == other.age && favoriteFood.equals(other.favoriteFood) &&
name.equals(other.name);
}
}
JUnit 4.11+ (or JUnit 5) both as test runner and assertion tool
#Test
void assertListNotEquals_JUnit_way() {
Animal scoubi = new Animal("scoubi", 10, "hay");
Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
Assert.assertNotEquals(scoubi, littleScoubi);
}
The test fails as expected but the cause provided to the developer is really not helpful. It just says that the values should be different and output the toString() result invoked on the actual Animal parameter :
java.lang.AssertionError: Values should be different. Actual: Animal
[name=scoubi, age=10, favoriteFood=hay]
at org.junit.Assert.fail(Assert.java:88)
Ok the objects are not equals. But where is the problem ?
Which field is not correctly valued in the tested method ? One ? Two ? All of them ?
To discover it you have to dig in the createWithNewNameAndAge() implementation/use a debugger while the testing API would be much more friendly if it would make for us the differential between which is expected and which is gotten.
JUnit 4.11 as test runner and a test Matcher API as assertion tool
Here the same scenario of test but that uses AssertJ (an excellent test matcher API) to make the assertion of the Animal state: :
import org.assertj.core.api.Assertions;
#Test
void assertListNotEquals_AssertJ() {
Animal scoubi = new Animal("scoubi", 10, "hay");
Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
Assertions.assertThat(littleScoubi)
.extracting(Animal::getName, Animal::getAge, Animal::getFavoriteFood)
.containsExactly("little scoubi", 1, "hay");
}
Of course the test still fails but this time the reason is clearly stated :
java.lang.AssertionError:
Expecting:
<["scoubi", 10, "hay"]>
to contain exactly (and in same order):
<["little scoubi", 1, "hay"]>
but some elements were not found:
<["little scoubi", 1]>
and others were not expected:
<["scoubi", 10]>
at junit5.MyTest.assertListNotEquals_AssertJ(MyTest.java:26)
We can read that for Animal::getName, Animal::getAge, Animal::getFavoriteFood values of the returned Animal, we expect to have these value :
"little scoubi", 1, "hay"
but we have had these values :
"scoubi", 10, "hay"
So we know where investigate : name and age are not correctly valued.
Additionally, the fact of specifying the hay value in the assertion of Animal::getFavoriteFood() allows also to more finely assert the returned Animal. We want that the objects be not the same for some properties but not necessarily for every properties.
So definitely, using a matcher API is much more clear and flexible.
Modulo API consistency, why JUnit didn't provide assertNotEquals() is the same reason why JUnit never provided methods like
assertStringMatchesTheRegex(regex, str) vs. assertStringDoesntMatchTheRegex(regex, str)
assertStringBeginsWith(prefix, str) vs. assertStringDoesntBeginWith(prefix, str)
i.e. there's no end to providing a specific assertion methods for the kinds of things you might want in your assertion logic!
Far better to provide composable test primitives like equalTo(...), is(...), not(...), regex(...) and let the programmer piece those together instead for more readability and sanity.