如何订购@Before方法

时间:2011-11-02 19:59:08

标签: scala junit junit4

我有一个特性,在块之前添加了几个测试。具体实例的@Before块在特征中的那些块之前运行。糟糕,这意味着我无法截断数据库表,然后插入fixture:

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方法,但是我必须记住让我的所有子类都调用截断方法。

在Scala 2.9.0.1上使用JUnit 4.10。

2 个答案:

答案 0 :(得分:4)

执行此操作的正确方法是使用@Rule,扩展@ExternalResource以用于类型行为之前和之后(在Java语法中):

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

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

您可以使用@Rule(在4.10中引入)按顺序将多个@RuleChain连接在一起,并再次使用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版本并应用patch for @Rule

scala代码可能类似于:

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"
}

答案 1 :(得分:0)

怎么样:

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 是-a DatabaseTest