Как создать модульный тест конструкторов - PullRequest
34 голосов
/ 26 мая 2011

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

public class Money {
    public Money(long l) {
        this.value = l;
    }

    public Money(String s) {
        this.value = toLong(s);
    }

    public long getLong() {
        return this.value;
    }

    public String getString() {
        return toString(this.value);
    }
}

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

Я пытаюсь выяснить, каков наиболее подходящий способ проверки этих конструкторов.

Должен ли быть тест для каждого конструктора и тип вывода:

@Test
public void longConstructor_getLong_MatchesValuePassedToConstructor() {
    final long value = 1.00l;

    Money m = new Money(value);
    long result = m.getLong();

    assertEquals(value, result);
}

Это приводит к множеству разных тестов. Как видите, я изо всех сил пытаюсь назвать их.

Если должно быть несколько утверждений:

@Test
public void longConstructor_outputsMatchValuePassedToConstructor() {
    final long longValue = 1.00l;
    final String stringResult = "1.00";

    Money m = new Money(longValue);

    assertEquals(longValue, m.getLong());
    assertEquals(stringResult, m.getString());
}

Это имеет несколько утверждений, что делает меня неудобным. Он также тестирует getString (и через proStist toString), но не указывает это в имени теста. Назвать их еще сложнее.

Я ошибаюсь, сосредоточившись на конструкторах. Должен ли я просто проверить методы конвертации? Но тогда следующий тест пропустит метод toLong.

@Test
public void getString_MatchesValuePassedToConstructor() {
    final long value = 1.00;
    final String expectedResult = "1.00";

    Money m = new Money(value);
    String result = m.getLong();
    assertEquals(expectedResult, result);
}

Это устаревший класс, и я не могу изменить исходный класс.

Ответы [ 7 ]

15 голосов
/ 26 мая 2011

Похоже, у вас есть канонический способ получения «сырого» значения (в данном случае toLong), поэтому просто проверьте, что все конструкторы верны, когда вы получаете это значение.Затем вы можете протестировать другие методы (например, getString()), основанные на одном конструкторе, поскольку вы знаете, что после завершения работы различных конструкторов все они оставляют объект в одном и том же состоянии.

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

13 голосов
/ 26 мая 2011

Ожидаемый результат от теста конструктора: был создан экземпляр

Следуя этой идее, вы можете ограничить работу в тестах конструктора чистой реализацией:

@Test public void testMoneyString() {
    try {
      new Money("0");
      new Money("10.0");
      new Money("-10.0");
    } catch (Exception e) {
      fail(e.getMessage());
    }
}

@Test public void testMoneyStringIllegalValue() {
    try {
      new Money(null);
      fail("Exception was expected for null input");
    } catch (IllegalArgumentException e) {          
    }

    try {
      new Money("");
      fail("Exception was expected for empty input");
    } catch (IllegalArgumentException e) {          
    }

    try {
      new Money("abc");
      fail("Exception was expected for non-number input");
    } catch (IllegalArgumentException e) {          
    }

}

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

11 голосов
/ 26 мая 2011

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

@Test
public void moneyConstructorThatTakesALong {
3 голосов
/ 26 мая 2011

Я думаю, вы слишком много времени думаете об этом.Все ваши варианты работают просто отлично, поэтому просто выберите тот, который вам нравится больше всего.Не забывайте, что цель - дать вам уверенность в том, что код будет работать так, как задумано / ожидаемо.Каждый из этих сценариев обеспечит это.

Лично в таком простом случае я бы пошел с одним тестовым примером, который проверяет конструкторы.Это устраняет необходимость в чрезмерных и довольно громоздких именах методов.

2 голосов
/ 26 мая 2011

Это не ПЛОХАЯ идея тестировать конструкторы просто для того, чтобы убедиться, что данные воспринимаются как требуется, но если вам действительно не нравятся множественные утверждения, разделите их и назовите метод, который они делают, например: CanContructorAcceptString или:CanConstructorAcceptNonLongStringValue

Что-то в этом роде.

0 голосов
/ 09 ноября 2018

Конструктор является инициализатором объекта.Конструкторское тестирование - это проверка факта инициализации.То есть проверка того, что поля объекта были инициализированы значениями, переданными в конструктор.И это может быть подтверждено только отражением.

0 голосов
/ 26 мая 2011

У вас есть несколько входов (через конструкторы) и несколько выходов (через разные методы getX ()). Но число членов, которые оно имеет внутри, кажется меньше (в вашем примере 1 длинное значение). Разве не было бы проще сначала проверить различные входные данные, создав x различных объектов, используя различные конструкторы x . Затем вы можете проверить, все ли они равны, используя реализованный метод equals (). Это можно сделать одним методом испытаний.

Затем вы можете проверить возможные методы получения один за другим, не используя все разные конструкторы.

Конечно, это требует от вас реализации (отдельного теста) метода equals.

В вашем примере я бы создал следующие тестовые случаи:

@Test
public void testEquals() {
    Money m1 = new Money(1);
    Money m2 = new Money(1);
    Money m3 = new Money(2);

    assertEquals(m1, m2);
    assertEquals(m2, m1);
    assertNotEquals(m1, m3);
    assertNotEquals(m3, m1);
    assertNotEquals(m1, null);
}

private void testConstructors(long lValue, String sValue) {
    Money m1 = new Money(lValue);
    Money m2 = new Money(sValue);

    assertEquals(m1, m2);
}

@Test
public void testConstructorsPositive() {
    testConstructors(1, "1");
}

@Test
public void testConstructorsNegative() {
    testConstructors(-1, "-1");
}

@Test
public void testConstructorsZero() {
    testConstructors(0, "0");
}

@Test
public void testGet...() { /* etc... */ }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...