Как добавить тестовое покрытие для частного конструктора? - PullRequest
101 голосов
/ 23 декабря 2010

Это код:

package com.XXX;
public final class Foo {
  private Foo() {
    // intentionally empty
  }
  public static int bar() {
    return 1;
  }
}

Это тест:

package com.XXX;
public FooTest {
  @Test 
  void testValidatesThatBarWorks() {
    int result = Foo.bar();
    assertEquals(1, result);
  }
  @Test(expected = java.lang.IllegalAccessException.class)
  void testValidatesThatClassFooIsNotInstantiable() {
    Class cls = Class.forName("com.XXX.Foo");
    cls.newInstance(); // exception here
  }
}

Работает нормально, класс протестирован.Но Кобертура говорит, что у частного конструктора класса нет покрытия кода.Как мы можем добавить тестовое покрытие для такого частного конструктора?

Ответы [ 16 ]

1 голос
/ 11 марта 2011

Другой вариант - создать статический инициализатор, подобный следующему коду

class YourClass {
  private YourClass() {
  }
  static {
     new YourClass();
  }

  // real ops
}

Таким образом, закрытый конструктор считается проверенным, и накладные расходы времени выполнения в основном не поддаются измерению. Я делаю это, чтобы получить 100% покрытие с помощью EclEmma, ​​но, вероятно, это работает для каждого инструмента покрытия. Недостаток этого решения, конечно, заключается в том, что вы пишете производственный код (статический инициализатор) только для целей тестирования.

1 голос
/ 23 декабря 2010

Я не знаю о Cobertura, но я использую Clover, и у него есть средство для добавления исключений по шаблону.Например, у меня есть шаблоны, которые исключают строки регистрации apache-commons, поэтому они не учитываются в покрытии.

0 голосов
/ 01 апреля 2013
@Test
public void testTestPrivateConstructor() {
    Constructor<Test> cnt;
    try {
        cnt = Test.class.getDeclaredConstructor();
        cnt.setAccessible(true);

        cnt.newInstance();
    } catch (Exception e) {
        e.getMessage();
    }
}

Test.java - ваш исходный файл с вашим личным конструктором

0 голосов
/ 06 августа 2012

Если бы я угадал цель вашего вопроса, я бы сказал:

  1. Требуются разумные проверки для частных конструкторов, выполняющих реальную работу, и
  2. Вы хотите, чтобы Clover исключил пустые конструкторы для классов util.

Для 1 очевидно, что вы хотите, чтобы вся инициализация выполнялась с помощью заводских методов. В таких случаях ваши тесты должны быть в состоянии проверить побочные эффекты конструктора. Это должно подпадать под категорию обычного частного метода тестирования. Уменьшите методы так, чтобы они выполняли только ограниченное количество определенных вещей (в идеале, только одно и одно хорошо), а затем протестируйте методы, которые на них полагаются.

Например, если мой [приватный] конструктор устанавливает поля экземпляра моего класса от a до 5. Тогда я могу (или, скорее, должен) проверить это:

@Test
public void testInit() {
    MyClass myObj = MyClass.newInstance(); //Or whatever factory method you put
    Assert.assertEquals(5, myObj.getA()); //Or if getA() is private then test some other property/method that relies on a being 5
}

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

<clover-setup initString="${build.dir}/clovercoverage.db" enabled="${with.clover}">
    <methodContext name="prvtCtor" regexp="^private *[a-zA-Z0-9_$]+Util *( *) *"/>
</clover-setup>

Я намеренно пропустил .* после ), потому что такие конструкторы не предназначены для создания исключений (они не должны ничего делать).

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

<clover-setup initString="${build.dir}/clovercoverage.db" enabled="${with.clover}">
    <methodContext name="prvtCtor" regexp="^private *[a-zA-Z0-9_$]+Util *( *) *"/>
    <methodContext name="myExceptionalClassCtor" regexp="^private MyExceptionalClass()$"/>
</clover-setup>

Если у вас много таких исключительных классов, вы можете изменить обобщенный приватный конструктор reg-ex, который я предложил, и удалить из него Util. В этом случае вам придется вручную убедиться, что побочные эффекты вашего конструктора все еще проверены и покрыты другими методами в вашем классе / проекте.

<clover-setup initString="${build.dir}/clovercoverage.db" enabled="${with.clover}">
    <methodContext name="prvtCtor" regexp="^private *[a-zA-Z0-9_$]+ *( *) .*"/>
</clover-setup>
0 голосов
/ 23 декабря 2010

Вы не можете.

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

0 голосов
/ 23 декабря 2010

Иногда Cobertura помечает код, который не предназначен для выполнения, как «не покрытый», в этом нет ничего плохого. Почему вас беспокоит наличие 99% покрытия вместо 100%?

Технически, однако, вы все равно можете вызывать этот конструктор с отражением, но для меня это звучит очень неправильно (в данном случае).

...