JUnit이 assertNotEquals 메소드를 제공하지 않는 이유는 무엇입니까?


429

JUnit 4가 왜 메소드를 제공 assertEquals(foo,bar)하지만 제공 하지 않는지 아는 사람이 assertNotEqual(foo,bar)있습니까?

을 제공하고 assertNotSame(에 해당 assertSame)assertFalse (에 해당하는 assertTrue그들이 포함 귀찮게하지 않았다 이상한 것 같다, 그래서) assertNotEqual.

그건 그렇고, JUnit-addons가 내가 찾고있는 방법을 제공한다는 것을 알고 있습니다. 나는 단지 호기심을 요구하고 있습니다.


JUnit 4.12 이후로는 assertNotEquals가 제공됩니다. junit.org/junit4/javadoc/4.12/org/junit/…
WebF0x

답변:


403

assertThat()모든 종류의 부정을 쉽게 설명 할 수 있고 어설 션이 실패한 경우 예상 한 것과 설명을 자동으로 작성할 수 있는 최신 스타일 어설트 를 사용하는 것이 좋습니다 .

assertThat(objectUnderTest, is(not(someOtherObject)));
assertThat(objectUnderTest, not(someOtherObject));
assertThat(objectUnderTest, not(equalTo(someOtherObject)));

세 가지 옵션은 모두 동일하므로 가장 읽기 쉬운 옵션을 선택하십시오.

메소드의 단순 이름을 사용하고 (이 시제 구문이 작동하도록하려면) 다음 가져 오기가 필요합니다.

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

134
대체 어설 션 구문에 대한 포인터에 감사하지만 다른 곳을 가리키는 것은 JUnit이 제공 하지 않았 는지 대답하지 않습니다 assertNotEquals().
seh

14
@ seh : 내가 읽은 방식은 역사적 관심이 아니라 JUnit 테스트에서 "이 두 객체가 동일하지 않다"는 주장을 공식화하는 방법에 관한 것입니다. 나는 대답했다. "왜 존재 하는가 / 없는가 assertNotEqual"를 고려할 때, 나는 그것이 자주 필요하지 않은 특수한 주장이기 때문에 assertEqualsgeneric을 통해 표현되기 때문 assertFalse이다.
Joachim Sauer

21
"가장 읽기 쉬운 것을 선택하십시오". 단위 테스트를 읽고 쓰는 사람들은 프로그래머입니다. 그들은 실제로 assertNotEqual (objectUnderTest, someOtherObject) 또는 assertFalse (objectUnderTest.equals (someOtherObject))보다 더 읽기 쉬운가? 나는 멋진 매처 API에 확신이 없다-프로그래머가 그것들을 사용하는 방법을 탐구 / 발견하는 것이 상당히 어려워 보인다.
bacar

@ bacar : 일부 주장에 따르면 기본적으로 스타일의 문제입니다. 그러나 사용 가능한 assertThat제한된 assert*방법 세트보다 훨씬 표현력이 좋습니다 . 따라서 당신은 한 줄에 정확한 제약을 표현이 (거의) 영어 문장처럼 읽을 수 있습니다 어설 션이 실패 할 때 의미있는 메시지가 표시됩니다. 물론, 이것은 항상 살인자 기능은 아니지만 몇 번 작동하는 것을 보았을 때 그 가치가 얼마나 큰지 알게 될 것입니다.
Joachim Sauer

5
@Joachim 나는 그것이 assertThat보다 표현력이 뛰어나다는 것에 동의 assert*하지만, assert*일반적으로 표현식 내부와 외부에 넣을 수있는 Java 표현식보다 더 표현력이 있다고 생각하지는 않습니다 (결국 Java 코드로 무엇이든 표현할 수 있음). 유창한 스타일의 API를 사용하기 시작한 것은 일반적인 문제입니다. 모든 것은 기본적으로 배우는 새로운 DSL입니다 (우리 모두 Java를 이미 알고있는 경우). 나는 사람들이 그것을 알기를 기대하는 것이 합리적이기 때문에 Hamcrest가 지금 어디에서나 충분히 보편적이라고 생각합니다. 나는 놀이를 할거야 ...
bacar

154

assertNotEqualsJUnit을 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;

1
jmock (2.6.0) maven 아티팩트 중 하나는 junit-dep의 이전 버전을 유출하여 assertNotEquals가없는 Assert 클래스를 갖습니다. 아이비를 사용할 때 제외하십시오.
gkephorus

7
4.12를 사용하고 있지만 여전히 assertNotEqual을 찾을 수 없습니다. : s
Mubashar

49

나도 똑같아 Assert API는 그리 대칭 적이 지 않습니다. 객체가 동일한 지 테스트하기 위해 다음을 제공합니다.assertSameassertNotSame .

물론 작성하기에는 너무 길지 않습니다.

assertFalse(foo.equals(bar));

이러한 주장으로 유감스럽게도 출력의 유익한 부분은 테스트 방법의 이름이므로 설명 메시지를 별도로 작성해야합니다.

String msg = "Expected <" + foo + "> to be unequal to <" + bar +">";
assertFalse(msg, foo.equals(bar));

물론 너무 지루해서 자신의 것을 굴리는 것이 좋습니다 assertNotEqual. 다행히 향후 JUnit의 일부가 될 것입니다. JUnit issue 22


19
그러나 JUnit이 예를 들어, foo와 bar의 값이 같지 않다고 알려주는 유용한 실패 메시지를 생성 할 수 없기 때문에 이것은 유용하지 않습니다. 실제 실패 이유는 숨겨져 있으며 간단한 부울로 바뀝니다.
벤 제임스

3
전적으로 동의합니다. 특히 assertFalse는 실제로 무엇이 잘못되었는지 출력하기 위해 적절한 메시지 인수가 필요합니다.
Mikko Maunu

나는 이것이 현재 텍스트 테스트에 유용하다고 생각합니다. Thnx
Marouane Gazanayi

텍스트의 문제점은 코드가 발전함에 따라 텍스트가 오래되었다는 것입니다.
Mark Levison

13

assertNotEqual이 없다는 것은 실제로 비대칭이며 JUnit을 조금 덜 배우게 만든다고 주장합니다. 메소드를 추가 할 때 API의 복잡성이 줄어든다는 사실을 명심하십시오. 대칭은 더 큰 공간을 지배하는 데 도움이됩니다. 내 생각 엔 생략이 필요한 이유는이 방법을 요구하는 사람들이 너무 적기 때문일 수있다. 그러나 assertFalse조차 존재하지 않았던 때를 기억합니다. 그러므로 나는 그 방법이 어려운 방법이 아니라면 결국 그 방법이 추가 될 것이라는 긍정적 인 기대를 가지고있다. 비록 많은 해결 방법, 심지어 우아한 해결 방법이 있음을 인정하지만.


7

나는이 파티에 꽤 늦게오고 있지만 나는 그 형태를 발견했다.

static void assertTrue(java.lang.String message, boolean condition) 

대부분의 '같지 않은'경우에 작동하도록 만들 수 있습니다.

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;

4
이것이 작동하지만 문제는 어설 션이 실패하면 단순히 "예상 되었으나 거짓"이라고 말하거나 다른 불분명 한 진술이라고 말할 것입니다. 그것이 123이 아니라 123이었던 것으로 예상된다면 가장 좋을 것입니다.
Stealth Rabbi

6

jUnit4.12를 사용하여 Java 8 환경에서 JUnit을 작업 중입니다.

나를 위해 : 컴파일러는 내가 사용했을 때도 assertNotEquals 메소드를 찾을 수 없었습니다.
import org.junit.Assert;

그래서 나는 변경
assertNotEquals("addb", string);

Assert.assertNotEquals("addb", string);

따라서 assertNotEqual인식되지 않는 문제에 직면하고 있다면 문제를 Assert.assertNotEquals(,);해결 하도록 변경하십시오.


1
메서드가 정적이기 때문에 정적으로 가져와야합니다. 이것을 사용 import static org.junit.Assert.*;하면 클래스 이름없이 모든 어설 션을 사용할 수 있습니다.
Tom Stone

3

사람들이 assertNotEquals ()를 원했던 명백한 이유는 내장 객체를 먼저 날아간 객체로 변환하지 않고 비교하는 것이 었습니다.

자세한 예 :

....
assertThat(1, not(equalTo(Integer.valueOf(winningBidderId))));
....

vs.

assertNotEqual(1, winningBidderId);

슬프게도 Eclipse에는 기본적으로 JUnit 4.11이 포함되어 있지 않으므로 자세한 정보가 필요합니다.

주의 사항 '1'을 Integer.valueOf ()에 싸야한다고 생각하지 않지만 .NET에서 새로 반환되었으므로 정확성에 의존하지 않습니다.


1

테스트 보고서에서 어설 션 실패에 대한 차이점을 보여주는 것처럼 assertFalse보다는 네거티브 어설 션에 Hamcrest를 사용하는 것이 좋습니다.

assertFalse를 사용하면 보고서에서 어설 션 오류가 발생합니다. 즉, 실패 원인에 대한 정보가 손실되었습니다.


1

일반적으로 두 객체가 같을 것으로 예상 할 때이 작업을 수행합니다.

assertTrue(obj1.equals(obj2));

과:

assertFalse(obj1.equals(obj2));

그들이 같지 않을 때 이것이 귀하의 질문에 대한 답변이 아니라는 것을 알고 있습니다. JUnit 4.11 이전의 JUnit 버전에서 수행 할 수있는 작업을 다른 사람이 검색하는 데 도움이 될 수 있습니다.


0

나는 OP 관점에 전적으로 동의합니다. Assert.assertFalse(expected.equals(actual))불평등을 표현하는 자연스러운 방법은 아닙니다.
그러나 나는 Assert.assertEquals(), Assert.assertNotEquals()작동하지만 테스트가 실제로 주장하는 것을 문서화하고 어설 션이 실패함에 따라 이해 / 디버그하는 것은 사용자 친화적이지 않다고 주장합니다.
JUnit 4.11과 JUnit 5는 Assert.assertNotEquals()(Assertions.assertNotEquals() JUnit 5에서) 제공하지만 실제로는 사용하지 않습니다.

대안으로, 객체의 상태를 주장하기 위해 나는 일반적으로 객체 상태를 쉽게 파헤치며 어설 션의 의도를 명확하게 문서화하고 어설 션 실패의 원인을 이해하는 데 매우 친숙한 matcher API를 사용합니다.

다음은 예입니다. 이름과 나이를 변경하면서 가장 좋아하는 음식을 유지하여 새 Animal 객체를 만드는 방법 인
테스트 할 Animal 클래스가 있다고 가정 createWithNewNameAndAge()합니다. 원본과 새 객체가 다르다고 주장하는 데
사용한다고 가정 Assert.assertNotEquals()합니다.
다음은 결함이있는 Animal 클래스입니다 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+ (또는 JUnit 5)

@Test
void assertListNotEquals_JUnit_way() {
    Animal scoubi = new Animal("scoubi", 10, "hay");
    Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
    Assert.assertNotEquals(scoubi, littleScoubi);
}

테스트가 예상대로 실패하지만 개발자에게 제공된 원인은 실제로 도움이되지 않습니다. 그냥 값이 달라야하고 toString()실제 Animal매개 변수 에서 호출 된 결과를 출력한다고 말합니다 .

java.lang.AssertionError : 값이 달라야합니다. 실제 : 동물

[이름 = scoubi, 나이 = 10, 가장 좋아하는 음식 = hay]

org.junit.Assert.fail (Assert.java:88)에서

좋아, 개체가 같지 않습니다. 그러나 문제는 어디에 있습니까?
테스트 된 방법에서 어떤 필드가 올바르게 평가되지 않습니까? 하나? 둘? 그들 모두?
그것을 발견하기 위해서는 createWithNewNameAndAge() 구현에서 디버거를 사용하고 디버거를 사용해야합니다. 테스트 API가 우리에게 기대되는 것과 얻는 것의 차이를 만들면 훨씬 친숙합니다.


테스트 러너 인 JUnit 4.11 및 어설 션 도구 인 테스트 매처 API

다음은 테스트 시나리오와 동일하지만 AssertJ (우수한 테스트 매처 API)를 사용하여 Animal상태 를 확인합니다 . :

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");
}

물론 테스트는 여전히 실패하지만 이번에는 그 이유가 명확하게 명시되어 있습니다.

java.lang.AssertionError :

기대 :

<[ "scoubi", 10, "hay"]>

정확히 (그리고 같은 순서로) 포함하기 :

<[ "little scoubi", 1, "hay"]>

그러나 일부 요소가 발견되지 않았습니다.

<[ "little scoubi", 1]>

다른 사람들은 예상하지 못했습니다.

<[ "scoubi", 10]>

junit5.MyTest.assertListNotEquals_AssertJ (MyTest.java:26)에서

우리는 대한 것을 읽을 수 있습니다 Animal::getName, Animal::getAge, Animal::getFavoriteFood반환 된 동물의 값, 우리는 이러한 가치가 기대 :

"little scoubi", 1, "hay" 

그러나 우리는 다음과 같은 가치를 가지고 있습니다.

"scoubi", 10, "hay"

우리는 어디에서 조사를 알 수 있도록 : name그리고 age제대로 평가되지 않습니다. 또한 hay어설 션에 값 을 지정 Animal::getFavoriteFood()하면 반환 된을보다 세밀하게 어설 션 할 수 있습니다 Animal. 우리는 객체가 일부 속성에 대해 동일하지는 않지만 모든 속성에 대해 반드시 필요한 것은 아닙니다.
따라서 matcher API를 사용하는 것이 훨씬 명확하고 유연합니다.


-1

모듈로 API 일관성, JUnit이 제공하지 않은 이유는 JUnit assertNotEquals()이 다음과 같은 메소드를 제공하지 않은 것과 같은 이유입니다.

  • assertStringMatchesTheRegex(regex, str) vs. assertStringDoesntMatchTheRegex(regex, str)
  • assertStringBeginsWith(prefix, str) vs. assertStringDoesntBeginWith(prefix, str)

즉, 어설 션 논리에서 원하는 종류의 항목에 대해 특정 어설 션 방법을 제공 할 필요가 없습니다!

훨씬 더 나은처럼 가능한 테스트 프리미티브를 제공하기 위해 equalTo(...), is(...), not(...), regex(...)더 가독성과 정신 대신 함께 프로그래머 조각을들을 수 있습니다.


3
글쎄, 어떤 이유로 assertEquals ()가 존재합니다. 꼭 그럴 필요는 없었습니다. 문제는 대칭의 부족에 관한 것이 었습니다. 왜 assertEquals가 존재하지만 그에 상응하지는 않습니까?
foo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.