ScalaTest: проблемы с повторной инициализацией Singleton Object - PullRequest
9 голосов
/ 05 августа 2010

Я тестирую парсер, написанный на Scala, используя ScalaTest.Синтаксический анализатор обрабатывает один файл за раз, и у него есть одноэлементный объект, подобный следующему:

class Parser{...}
object Resolver {...}

Написанный мной тест выглядит примерно так:

   describe("Syntax:") {
    val dir = new File("tests\\syntax");
    val files = dir.listFiles.filter(
                    f => """.*\.chalice$""".r.findFirstIn(f.getName).isDefined);

    for(inputFile <- files) {
      val parser = new Parser();
      val c = Resolver.getClass.getConstructor();
      c.setAccessible(true);
      c.newInstance();

      val iserror = errortest(inputFile)
      val result = invokeparser(parser,inputFile.getAbsolutePath) //local method
      it(inputFile.getName + (if (iserror)" ERR" else " NOERR") ){
      if (!iserror) result should be (ResolverSuccess()) 
        else if(result.isInstanceOf[ResolverError]) assert(true)
      }
    }
  }

Теперь на каждой итерацииПобочные эффекты предыдущих итераций внутри одноэлементного объекта Resolver не устранены.

Есть ли способ указать для самого масштабируемого модуля повторную инициализацию одноэлементных объектов?

Обновление : по предложению Даниэля я обновил код, также добавил ещедетали.

Обновление : Видимо, это парсер, который делает что-то подозрительное.При последующих вызовах не сбрасывает предыдущий AST.странный.поскольку это не по теме, я бы больше копался и, вероятно, использовал отдельную ветку для обсуждения, спасибо всем за ответы

Окончательное обновление : проблема была с одноэлементным объектом, отличным от Resolver,это было в каком-то другом файле, поэтому я как-то пропустил это.Я смог решить это, используя ответ Даниэля Спивака.Это грязный способ делать вещи, но это также единственное, учитывая мои обстоятельства, а также учитывая тот факт, что я пишу тестовый код, который не будет использоваться в производстве.

Ответы [ 2 ]

7 голосов
/ 05 августа 2010

Согласно спецификации языка, нет возможности воссоздать одноэлементные объекты.Однако позволяет рефлексивно вызывать конструктор синглтона, который перезаписывает внутреннее поле MODULE$, которое содержит фактическое значение синглтона:

object Test

Test.hashCode    // => e.g. 779942019

val c = Test.getClass.getConstructor()
c.setAccessible(true)
c.newInstance()

Test.hashCode    // => e.g. 1806030550

Теперь, когда я поделилсязлой секрет с тобой, позволь мне предупредить тебя никогда, никогда делать это.Я бы очень старался изменить код, вместо того, чтобы играть хитрые трюки, подобные этому.Однако, если все идет так, как вы говорите, и у вас действительно нет другого выбора, это по крайней мере что-то.

4 голосов
/ 05 августа 2010

ScalaTest имеет несколько способов, позволяющих вам повторно инициализировать вещи между тестами. Однако на этот конкретный вопрос сложно ответить, не зная больше. Главный вопрос заключается в том, что нужно для повторной инициализации одноэлементного объекта? Если одноэлементный объект не может быть повторно инициализирован без создания экземпляра нового одноэлементного объекта, вам необходимо убедиться, что каждый тест заново загружает одноэлементный объект, что потребует использования пользовательских загрузчиков классов. Мне трудно поверить, что кто-то спроектировал бы что-то таким образом. Можете ли вы обновить свой вопрос с более подробной информацией, как это? Позже я еще раз посмотрю и выясню, помогут ли дополнительные детали сделать ответ более очевидным.

ScalaTest имеет путь запуска, который загружает классы заново для каждого запуска, но не тестовый путь. Таким образом, вам придется свернуть свой собственный. Настоящая проблема здесь в том, что кто-то спроектировал это так, что его нелегко проверить. Я бы посмотрел на загрузку Resolver и Parser с URLClassLoader внутри каждого теста. Таким образом, вы будете получать новый Resolver каждый тест.

Вам нужно будет убрать Parser & Resolver из пути к классам и из пути к ним. Поместите их в свой каталог. Затем создайте URLClassLoader для каждого теста, который указывает на этот каталог. Затем вызовите findClass ("Parser") в этом загрузчике классов, чтобы получить его. Я предполагаю, что Parser ссылается на Resolver, и в этом случае JVM вернется к загрузчику классов, который загрузил Parser, чтобы получить Resolver, который является вашим URLClassLoader. Сделайте newInstance на Parser, чтобы получить экземпляр. Это должно решить вашу проблему, потому что вы будете получать новый одноэлементный объект Resolver для каждого теста.

...