Перегрузка статического импорта - PullRequest
3 голосов
/ 19 сентября 2009

В тестовом классе я хотел бы предоставить свою собственную перегрузку assertEquals с некоторой специальной логикой, не основанной на Object.equals. К сожалению, это не работает, потому что, как только я объявляю мой метод assertEquals локально, Java больше не находит статический импорт из org.junit.Assert.*.

Есть ли способ обойти это? То есть Есть ли способ обеспечить дополнительную перегрузку для статически импортированного метода? (Довольно очевидное решение состоит в том, чтобы называть метод по-другому, но это решение не имеет такой же эстетической привлекательности.)

Мой файл тестового класса выглядит примерно так:

package org.foo.bar;

import static org.junit.Assert.*;

import org.junit.Test;

public class BarTest {
    private static void assertEquals(Bar expected, Bar other) {
        // Some custom logic to test equality.
    }

    @Test
    public void testGetFoo() throws Exception {
        Bar a = new Bar();
        assertEquals(42, a.getFoo()); // Error *
    }

    @Test
    public void testCopyConstructor() throws Exception {
        Bar a = new Bar();
        // Fill a.
        Bar b = new Bar(a);
        assertEquals(a, b);
    }
}

Error * означает «Метод assertEquals(Bar, Bar) в типе BarTest не применим для аргументов (int, int)

Ответы [ 4 ]

3 голосов
/ 19 сентября 2009

В этом ответе есть два раздела - один об ошибке компиляции, а другой об использовании assertEquals ()

Проблема в том, что в двух разных пространствах имен есть два метода assertEquals () - один присутствует в пространстве имен org.junit.Assert, другой - в пространстве имен org.foo.bar.BarTest (текущее пространство имен).

Ошибка сообщается компилятором из-за правил теневого копирования , объявленных в спецификации языка Java . Статический импорт Assert.assertEquals () затеняется assertEquals (), объявленным в классе BarTest.

Исправление (всегда в случае теневых объявлений) заключается в использовании FQN (полных имен). Если вы собираетесь использовать assertEquals (...) класса JUnit Assert, используйте

org.junit.Assert.assertEquals(...)

и когда вам нужно использовать декларацию, просто используйте

assertEquals(...)

только в BarTest, где оно затенено. Во всех других классах, для которых требуется только Assert.assertEquals () или BarTest.asserEquals (), вы можете импортировать Assert или BarTest (я не думаю, что вам нужно будет импортировать BarTest в другое место, но, тем не менее, это указано).

Когда нет теневого копирования, вы можете позволить себе просто импортировать класс или статический метод и использовать его без FQN.

Дополнительные вещи, чтобы думать о

Assert.assertEquals () внутренне использует метод equals () классов аргументов. Объявление assertEquals () в вашем тестовом примере нарушает принцип DRY, так как метод типа equals () должен быть реализован и использоваться согласованно - две разные реализации в исходном коде и в модульных тестах неизбежно вызовут путаницу.

Наилучшим подходом было бы реализовать equals () на Bar, а затем использовать Assert.assertEquals () в ваших тестовых примерах. Если у вас уже есть, вам не нужен BarTest.assertEquals (). Псевдокод для assertEquals () похож на следующий

  1. Если оба аргумента равны нулю, вернуть true.
  2. Если Ожидается не равно нулю, то вызывать equals () для Ожидается , передавая фактический в качестве аргумента. Вернуть true, если объект равен.
  3. Если объекты не равны, выдается ошибка AssertionEr с отформатированным сообщением.
3 голосов
/ 19 сентября 2009

Одним из возможных решений для вашего конкретного примера вызова assertEquals(Bar, Bar) в модульном тесте было бы расширение класса классом, предоставляющим статический метод, следующим образом:

class BarAssert extends Assert {
  public static void assertEquals(Bar expected, Bar other) {
        // Some custom logic to test equality.
    }
}

Затем вы можете включить import static BarAssert.assertEquals; и использовать свою собственную логику.

Извиняюсь, что это не дает прямого ответа на вопрос, а больше нацелено на ваш пример. Согласно моему комментарию, приложенному к вопросу, я бы рекомендовал против такого подхода.

1 голос
/ 19 сентября 2009

Единственный способ - полностью квалифицировать один или другой.

import static org.junit.Assert.*;

import org.junit.Test;

public class BarTest {

    private static void assertEquals(Bar expected, Bar other) {
        // Some custom logic to test equality.
    }

    @Test
    public void testGetFoo() throws Exception {
        Bar a = new Bar();
        org.junit.Assert.assertEquals(42, a.getFoo());
    }
}
0 голосов
/ 19 сентября 2009
this.assertEquals(a,b);

или

BarTest.assertEquals(a,b);

Я бы пошел с первым, поскольку, несмотря на то, что это статический метод, у вас должен быть экземпляр, чтобы использовать его (он является частным), и this не будет подвержен прихотям будущих переименований. .

...