Есть ли в Grails хороший способ имитировать текущее время, используя время Joda?

Я пишу код, который вычисляет дату и время по отношению к текущему времени. Во времена Джоды доступ к этому осуществляется через конструктор (Java), поскольку это неизменяемый объект. Мне нужно иметь возможность имитировать, чтобы new DateTime() возвращал конкретный постоянный момент времени, чтобы я мог делать разумные тестовые утверждения, но не трогать все остальные методы DateTime.

Это оказывается противно. Grails mockFor(DateTime, true) не позволяет мне издеваться над конструктором Java, но нет очевидного или удобочитаемого неконструкторского способа получить время во времени Joda.

Единственные доступные варианты, по-видимому, связаны с низкоуровневыми методами JVM, такими как JMockit или насмешка класса EasyMock 3, которые являются болью от Grails. Есть ли простой/прямой способ добиться этого?


person Stuart Watt    schedule 25.05.2011    source источник


Ответы (3)


В итоге мы создали dateService методом now(). В модульных тестах мы издеваемся над ним с помощью

domainInstance.dateService = [ now: { currentTime } ] as DateService

где currentTime — поле класса модульного теста. Это накладывает зависимость всех от dateService (наша единственная почти глобальная зависимость), а для классов src приходится передавать ее вручную.

Юнит-тесты, OTOH, выглядят довольно ясно.

person Victor Sergienko    schedule 26.05.2011
comment
Отличный ответ. Я как бы надеялся избежать изменения архитектуры системы только для того, чтобы сделать этот тест возможным, но я много узнал о том, насколько болезненным может быть имитирование конструкторов. - person Stuart Watt; 26.05.2011
comment
Спасибо. Я стараюсь избегать прямого вызова конструктора - это нарушает код интерфейса, созданный экземпляр нельзя экстернализовать, как в тестах, и иногда приводит к Construction Blobs. Так что все сводилось к форме внедрения зависимостей. - person Victor Sergienko; 26.05.2011

Я знаю, что это уже было принято, но с Joda-time вы можете заморозить и настроить его так, как вам нравится. Таким образом, вы можете остановить время, продвинуть время вперед, вернуться назад во времени. Если вы постоянно используете Joda, ваши объекты будут получать «сейчас» в любое время, которое вы установили.

// Stop time (and set a particular point in time):
DateTimeUtils.setCurrentMillisFixed(whenever);

// Advance time by the offset:
DateTimeUtils.setCurrentMillisOffset(offsetFromCurrent);

// Restore time (you could do this in an @After method)
DateTimeUtils.setCurrentMillisSystem();
person alpian    schedule 26.05.2011
comment
Ооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооочень круто, я надеялся на что-то подобное. Это значительно упростит тестирование. Отличный совет!! - person Stuart Watt; 26.05.2011
comment
Теперь я чувствую себя глупо, потому что мы на Джоде. Это лучший способ. Хорошо, я кое-что узнал. - person Victor Sergienko; 29.05.2011

вы можете просто использовать старые добрые принципы объектно-ориентированного программирования, например.

  interface TimeService {
    DateTime getCurrentTime()

    // other time-related methods
  }

  class JodaTimeService implements TimeService {
    DateTime getCurrentTime() {
      new DateTime()
    }  
  }

  class MockTimeService implements TimeService {
    DateTime getCurrentTime() {
      // return a fixed point in time
    }  
  }

Ваш код должен получить ссылку на реализацию TimeService через внедрение зависимостей. В resources.groovy используйте MockTimeService только при запуске тестов

import grails.util.Environment

beans = {    
    if (Environment.current == Environment.TEST) {
        timeService(MockTimeService)

    } else {
         timeService(JodaTimeService)
    }
}
person Dónal    schedule 26.05.2011
comment
Независимо тот же подход, что и выше, большое спасибо, и хорошее решение проработано глубоко. И отдельное спасибо за советы по использованию тестовой среды для переключения. - person Stuart Watt; 26.05.2011