Перегрузка статического импорта

В тестовом классе я хотел бы предоставить свою собственную перегрузку 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 ответа

Решение

В этом ответе есть два раздела - один об ошибке компиляции, а другой об использовании 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. Если объекты не равны, выведите AssertionError с отформатированным сообщением.

Одно возможное решение для вашего конкретного примера вызова 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; и использовать вашу собственную логику.

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

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

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());
    }
}
this.assertEquals(a,b);

или же

BarTest.assertEquals(a,b);

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

Другие вопросы по тегам