2016-02-26 2 views
1

Я получаю странные результаты от теста Spock unit, который я назвал мыслью было вызвано неправильным использованием аннотации Groovy's TupleConstructor. Однако, благодаря помощи другого пользователя, я вижу, что это проблема с тем, как Spock создает mocks. Хотя я исправил проблему, заменив инъецированные mocks на реальные экземпляры, мне нужно фактически заставить насмехаться над этим.Spock Mock не работает для модульного теста

Мои основные классы:

@Canonical 
@TupleConstructor(callSuper = true) 
abstract class Vehicle { 
    Long id 
} 

@Canonical 
@TupleConstructor(callSuper = true, includeSuperProperties = true) 
abstract class Foobaz extends Vehicle { 
    String name 
    String label 
    String description 
} 

@Canonical 
@TupleConstructor(callSuper = true, includeSuperProperties = true) 
class Fizz extends Foobaz { 
    // This is an empty class that creates a meaningful name over the 
    // abstract Foobaz parent class. This may seem like bad design in 
    // this analogy, but I assure you it makes sense (from a Domain-Driven 
    // Design perspective) in my actual application. 
} 

@Canonical 
@TupleConstructor(callSuper = true, includeSuperProperties = true) 
class Car extends Vehicle { 
    Fizz fizz1 
    Fizz fizz2 

    @Override 
    String toString() { 
     "${fizz1.name} - ${fizz2.name}" 
    } 
} 

Мой Спок тест:

class CarSpec extends Specification { 
    def "toString() generates a correct string"() { 
     given: "a Car with some mocked dependencies" 
     String f1 = 'fizzy' 
     String f2 = 'buzzy' 
     Fizz fizz1 = Mock(Fizz) 
     Fizz fizz2 = Mock(Fizz) 

     fizz1.name >> f1 
     fizz2.name >> f2 

     Car car = new Car(1L, fizz1, fizz2) 

     when: "we call toString()" 
     String str = car.toString() 

     then: "we get a correctly formatted string" 
     "${f1} - ${f2}" == str 
    } 
} 

Но когда я запускаю это я получаю отказ/ошибка:

Condition not satisfied: 

"${f1} - ${f2}" == str 
    |  |  | | 
    fizzy buzzy | null - null 
       false 
       <omitting details here for brevity> 

Expected :null - null 

Actual :fizzy - buzzy 

Любые идеи, где я «Не так ли?

+1

Вы не предоставили класс Fizz. Я тестировал ваш пример с помощью простого интерфейса Fizz, и он работает –

+0

Спасибо @ JérémieB (+1) - я буду включать 'Fizz', если вы * абсолютно * считаете его необходимым, но так как они вводятся как Mocks, почему они имеют значение? – smeeb

+1

Вероятно, вы издеваетесь над fizz, что является причиной вашей ошибки. с 'Fizz {String getName()}', ваш пример работает. Так что это не связано с '@ TupleConstructor' –

ответ

2

С нашей дискуссии по различным одним из @ smeeb's questions, я изучил это немного больше, так как я был очень смущен, почему это не работает.

Я создал свой собственный тест.

class SomeTest extends Specification { 

    static class Driver { 

     String getName(Superclass superclass) { 
      return superclass.name 
     } 
    } 

    static abstract class Superclass { 
     String name 
    } 

    static class Subclass extends Superclass { 
    } 


    def 'test'() { 
     given: 
     def driver = new Driver() 
     def subclass = Mock(Subclass) 

     subclass.name >> 'test' 

     expect: 
     driver.getName(subclass) == 'test' 
    } 
} 

Не удалось с той же проблемой, что и @smeeb.

driver.getName(subclass) == 'test' 
|  |  |   | 
|  null |   false 
|    Mock for type 'Subclass' named 'subclass' 

Я попытался изменить несколько различных вещей, и обнаружил, что, когда я либо удален модификатор abstract из Superclass или изменил return superclass.name на return superclass.getName() тест начал работать.

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

Таким образом, в вашем случае либо удалить abstract модификатор из FooBaz или изменить свой код:

@Override 
String toString() { 
    "${fizz1.getName()} - ${fizz2.getName()}" 
} 
+1

Так что в основном Groovy и Spock слишком причудливы друг для друга. Мне нравится Groovy и все, но g * d d * mn, если я не пропущу свой тип безопасности! – smeeb

+2

Я чувствую то же самое. Я не могу найти этот конкретный случай документально, но я столкнулся с другими Groovy gotchas за последние пару лет, что я потратил несколько часов, чтобы найти «не исправит» биг-код на своем трекере. В частности, речь шла о вызове закрытия в методе абстрактного суперкласса из подкласса, что привело к тому, что закрытие не имело доступа к закрытым полям и методам суперкласса. В Java 8 lambdas нет этой проблемы. Я в процессе получения опыта с Kotlin (если у Скалы и Groovy был ребенок), поэтому я могу рекомендовать его для моего следующего задания. –

+0

Спасибо @ Jon Я предложил глубокое погружение на этом! – smeeb

3

Если изменить спецификацию к этому:

class CarSpec extends Specification { 
    def "toString() generates a correct string"() { 
     given: "a Car with some mocked dependencies" 
     String f1 = 'fizzy' 
     String f2 = 'buzzy' 
     Fizz fizz1 = Mock() 
     Fizz fizz2 = Mock() 

     Car car = new Car(1L, fizz1, fizz2) 

     when: "we call toString()" 
     String str = car.toString() 

     then: "we get a correctly formatted string + getProperty('name') is called once on each Mock" 
     "$f1 - $f2" == str 

     1 * fizz1.getProperty('name') >> f1 
     1 * fizz2.getProperty('name') >> f2 
    } 
} 

Таким образом, можно определить взаимодействие в then блоке, то все это должно работать нормально ...

+0

Спасибо @tim_yates (+1) - Я попробую, но для меня это кажется немного назад + контратакой! Вы говорите, что вы подключаете свои издевательства ... после того, как уже делаете утверждения, используя их?!? В среде Java/Mockito вы создаете свои mocks, проводьте/настраиваете их (чтобы возвращать/вести себя, как вы хотите), и * затем * вы их используете. Это звучит, как в Spock-land, вы создаете свои издевки, используете их, а затем подключаете/настраиваете их?!? – smeeb

+0

Да. [Как в документации] (https://spockframework.github.io/spock/docs/1.0/interaction_based_testing.html#_mocking) –

+0

Право. Но этот пример просто проверяет *, @tim_yates. То, что вы предлагаете выше, - это * проверка + конфигурация *. Я не говорю, что ты ошибаешься, я говорю, что Спок странный и противоречивый. Кроме того, метод 'getProperty()' не отображается нигде в этой ссылке, поэтому это не просто «в документации». – smeeb

Смежные вопросы