Как заказать методы @Before

У меня есть трейт, добавляющий пару тестов и перед блоками. Блоки @Before конкретного экземпляра запускаются перед блоками в трейте. К сожалению, это означает, что я не могу обрезать таблицы базы данных, а затем вставлять фикстуры:

trait DatabaseTest {
  @Before
  def truncate() {
    // "TRUNCATE %s".format(tableName)
  }

  def tableName
}

class PersonasTest extends DatabaseTest {
  @Before
  def addPersona() {
    // "INSERT INTO %s VALUES (...)".format(tableName)
  }


  @Test
  def testRejectsInsertWhenAlreadyInTable() {
    // "INSERT INTO %s VALUES (...)".format(tableName)
  }

  def tableName = "personas"
}

testRejectsInsertWhenAlreadyInTable всегда будет успешным, потому что порядок выполнения будет следующим:

  • addPersona
  • truncate
  • testRejectsInsertWhenAlreadyInTable

Как правильно упорядочить блоки @Before, не накладывая слишком много ограничений на подклассы? Я всегда мог бы объявить truncate в трейте, а затем иметь метод @Before в подклассе, но тогда я должен помнить, что все мои подклассы должны вызывать этот метод усечения.

Использование JUnit 4.10 на Scala 2.9.0.1.


person François Beausoleil    schedule 02.11.2011    source источник


Ответы (2)


Правильный способ сделать это — использовать @Rule, расширить @ExternalResource для поведения типа до и после (в синтаксисе Java):

@Rule
public ExternalResource resource= new ExternalResource() {
        @Override
        protected void before() throws Throwable {
                myServer.connect();
        };

        @Override
        protected void after() {
                myServer.disconnect();
        };
};

Вы можете связать и упорядочить несколько @Rule вместе по порядку, используя @RuleChain (представленный в 4.10), опять же в синтаксисе java:

@Rule
public TestRule chain= RuleChain
                       .outerRule(new LoggingRule("outer rule")
                       .around(new LoggingRule("middle rule")
                       .around(new LoggingRule("inner rule");

Есть предостережение. Вы не можете указать общедоступное поле в scala (общедоступные поля обертываются методами доступа, а сами поля становятся приватными). JUnit проверяет, применяется ли правило @Rule к общедоступному полю. Исправление для изменения кода JUnit, чтобы можно было применять @Rule как к методам, так и к полям.

Это было исправлено (мной) и объединено с мастером, но, к сожалению, оно еще не выпущено: оно будет частью 4.11. Таким образом, у вас есть два варианта: использовать 4.11-SNAPSHOT или загрузить выпуск 4.10 и применить патч для @ Правило.

Скала-код может выглядеть примерно так:

trait DatabaseTest {
    def truncate(): TestRule = {
        new ExternalResource() {
            override def before() = {
                // "TRUNCATE %s".format(tableName)
            }
        }
    }

    def extra(): TestRule = {
        // return a no-op rule
    }

    @Rule def testRule() = new RuleChain(truncate(), extra())
    def tableName
}

class PersonasTest extends DatabaseTest {
  def extra(): TestRule {
        new ExternalResource() {
            override def before() = {
                // "INSERT INTO %s VALUES (...)".format(tableName)
            }
        }
  }

  @Test
  def testRejectsInsertWhenAlreadyInTable() {
    // "INSERT INTO %s VALUES (...)".format(tableName)
  }

  def tableName = "personas"
}
person Matthew Farwell    schedule 02.11.2011
comment
Где находится репозиторий моментальных снимков JUnit? Согласно build.xml github.com/KentBeck/junit/blob/master /build.xml#L306, я должен найти его на oss.sonatype.org/ content/repositories/snapshots, но там только 4.9.1. - person François Beausoleil; 03.11.2011
comment
Я думаю, что произошла ошибка. Похоже, что ночная сборка — это 4.9.1, а не 4.11-SNAPSHOT. Я написал сопровождающему по электронной почте. - person Matthew Farwell; 03.11.2011
comment
4.9.1-SNAPSHOT не содержит ChainRule, поэтому, вероятно, это 4.9.1, а не 4.11. Большое спасибо за твою помощь! - person François Beausoleil; 03.11.2011
comment
Извините, да, я уверен, что это сборка 4.9.1. Проблема с конфигурацией на сервере интеграции. - person Matthew Farwell; 03.11.2011

Как насчет :

abstract class DatabaseTest {

  // "TRUNCATE %s".format(tableName)

  def tableName
}

class PersonasTest extends DatabaseTest {
  @Before
  def addPersona() {
    // "INSERT INTO %s VALUES (...)".format(tableName)
  }

  @Test
  def testRejectsInsertWhenAlreadyInTable() {
    // "INSERT INTO %s VALUES (...)".format(tableName)
  }

  def tableName = "personas"
}

В конце концов, PersonasTest является DatabaseTest.

person Pablo Fernandez    schedule 02.11.2011