Может ли Мокито заглушить метод без учета аргумента?

303

Я пытаюсь протестировать старый код, используя Mockito.

Я хочу заглушить, FooDaoчто используется в производстве следующим образом:

foo = fooDao.getBar(new Bazoo());

Я могу написать:

when(fooDao.getBar(new Bazoo())).thenReturn(myFoo);

Но очевидная проблема заключается в том, что getBar()никогда не вызывается с тем же Bazooобъектом, для которого я обозначил метод. (Прокляни этого newоператора!)

Мне бы понравилось, если бы я мог заглушить метод таким образом, чтобы он возвращался myFooнезависимо от аргумента. Если это не удастся, я выслушаю другие предложения по обходному пути, но я действительно хотел бы избежать изменения производственного кода до тех пор, пока не будет разумного покрытия тестами.

Эрик Уилсон
источник

Ответы:

456
when(
  fooDao.getBar(
    any(Bazoo.class)
  )
).thenReturn(myFoo);

или (чтобы избежать nulls):

when(
  fooDao.getBar(
    (Bazoo)notNull()
  )
).thenReturn(myFoo);

Не забудьте импортировать совпадения (доступно много других):

Для Mockito 2.1.0 и новее:

import static org.mockito.ArgumentMatchers.*;

Для более старых версий:

import static org.mockito.Matchers.*;
Томаш Нуркевич
источник
2
Мне нравится, когда ответ предшествует концу «принять замораживание ответа».
Эрик Уилсон
10
Там есть notNull(Bazoo.class)что-то вроде any(Bazoo.class)(может быть, его не было на момент ответа)
Дандре Эллисон
2
я имел несколько особое положение , где я мог бы иметь одно из двух возможных аргументов - Bazooили Cazooкоторые являются подклассами, скажем, Azoo. потому что Bazooмне нужно было вернуться foo, но Cazooмне нужно было вернуться bar. в этой ситуации предлагаемое Matchers.any()решение не работает, однако Matchers.isA()работает отлично.
Танвир
3
org.mockito.Matchersтеперь устарела - используйте org.mockito.ArgumentMatchersвместо этого, т.е. import static org.mockito.ArgumentMatchers.*(см. документы )
DontDivideByZero
when(myFoo.knowsWhatsUp()).thenReturn(myMoney);
6
18

Используйте как это:

when(
  fooDao.getBar(
    Matchers.<Bazoo>any()
  )
).thenReturn(myFoo);

Прежде чем вам нужно импортировать Mockito.Matchers

Хамад
источник
1
Это запрещено!
DrB
15

http://site.mockito.org/mockito/docs/1.10.19/org/mockito/Matchers.html

anyObject() должно соответствовать вашим потребностям.

Также вы всегда можете рассмотреть возможность реализации hashCode()и equals()для Bazooкласса. Это заставит ваш пример кода работать так, как вы хотите.

Buhb
источник
Согласен со вторым предложением, но я все же предпочитаю не делать этого по нетехническим причинам.
Эрик Уилсон
1
Класс Matchers устарел (см. Документы - «Этот класс, скорее всего, будет удален в версии 3.0» )
Йоханнес Рабауэр,
1

Другой вариант - положиться на старый добрый equalsметод моды . Пока аргумент в whenmock equalsаргумент в тестируемом коде, то Mockito будет соответствовать макету.

Вот пример.

public class MyPojo {

    public MyPojo( String someField ) {
        this.someField = someField;
    }

    private String someField;

    @Override
    public boolean equals( Object o ) {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        MyPojo myPojo = ( MyPojo ) o;
        return someField.equals( myPojo.someField );
    }

}

затем, предполагая, что вы знаете, какое значение someFieldбудет иметь значение, вы можете сделать это так.

when(fooDao.getBar(new MyPojo(expectedSomeField))).thenReturn(myFoo);

Плюсы: это более явно, чем anyсовпадения. Как рецензент кода, я внимательно слежу за anyтем, что пишут младшие разработчики кода, поскольку он просматривает логику своего кода, чтобы сгенерировать соответствующий передаваемый объект.

con: Иногда поле, передаваемое объекту, является случайным идентификатором. В этом случае вы не можете легко создать ожидаемый объект аргумента в вашем фиктивном коде.

Другой возможный подход - использовать Answerобъект Mockito, который можно использовать с whenметодом. Answerпозволяет вам перехватить реальный вызов, проверить входной аргумент и вернуть фиктивный объект. В приведенном ниже примере я использую, anyчтобы перехватить любой запрос к методу. Но затем в Answerлямбде я могу дополнительно проверить аргумент Базо ... может быть, чтобы убедиться, что ему был передан правильный идентификатор. Я предпочитаю это anyсамому себе, чтобы по крайней мере какая-то проверка была сделана по аргументу.

    Bar mockBar = //generate mock Bar.

    when(fooDao.getBar(any(Bazo.class))
    .thenAnswer(  ( InvocationOnMock invocationOnMock) -> {
        Bazo actualBazo = invocationOnMock.getArgument( 0 );

        //inspect the actualBazo here and thrw exception if it does not meet your testing requirements.
        return mockBar;
    } );

Итак, чтобы подвести итог, я предпочитаю полагаться equals(где ожидаемый аргумент и фактический аргумент должны быть равны друг другу), и если равенство невозможно (из-за невозможности предсказать состояние фактического аргумента), я прибегну к чтобы Answerпроверить аргумент.

Хосе Мартинес
источник