Alay konusu bir yöntemin kendisine aktarılmış bir argüman döndürmesi


674

Aşağıdaki gibi bir yöntem imzası düşünün:

public String myFunction(String abc);

Mockito, yöntemin aldığı dizenin aynısını döndürmeye yardımcı olabilir mi?


Tamam, genel olarak herhangi bir java alaycı çerçeve hakkında ... Bu başka bir çerçeve ile mümkün mü, yoksa sadece istediğim davranışı taklit etmek için aptal bir saplama oluşturmak gerekir?
Abhijeet Kashnia

Yanıtlar:


1001

Mockito'da bir Cevap oluşturabilirsiniz. Diyelim ki myFunction yöntemiyle Application adlı bir arayüzümüz var.

public interface Application {
  public String myFunction(String abc);
}

İşte Mockito cevaplı test yöntemi:

public void testMyFunction() throws Exception {
  Application mock = mock(Application.class);
  when(mock.myFunction(anyString())).thenAnswer(new Answer<String>() {
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
      Object[] args = invocation.getArguments();
      return (String) args[0];
    }
  });

  assertEquals("someString",mock.myFunction("someString"));
  assertEquals("anotherString",mock.myFunction("anotherString"));
}

Mockito 1.9.5 ve Java 8'den beri lambda ifadesini de kullanabilirsiniz:

when(myMock.myFunction(anyString())).thenAnswer(i -> i.getArguments()[0]);

1
Ben de aradım. Teşekkür ederim! Benim sorunum farklıydı. Nesneleri depolayan ve ada göre döndüren bir kalıcılık hizmeti (EJB) alay etmek istiyorum.
migu

7
Cevabın oluşturulmasını saran ekstra bir sınıf oluşturdum. Kod şöyle okunurwhen(...).then(Return.firstParameter())
SpaceTrucker

69
Java 8 lambdas ile, belirli bir sınıf için bile, ilk argümanı döndürmek akşam yemeği kolaydır when(foo(any()).then(i -> i.getArgumentAt(0, Bar.class)). Ayrıca bir yöntem referansı kullanabilir ve gerçek yöntemi çağırabilirsiniz.
Paweł Dyda

Bu, bir yöntemdeki sorunumu Iterator<? extends ClassName>bir thenReturn()ifadede her türlü döküm sorununa neden olan dönüşleri çözer .
Mart'ta Michael Shopsin

16
Java 8 ve Mockito <1.9.5 ile Paweł'in cevabı olurwhen(foo(any()).thenAnswer(i -> i.getArguments()[0])
Graeme Moss

567

Mockito 1.9.5 veya daha üst sürümünüz varsa, Answernesneyi sizin için yapabilen yeni bir statik yöntem vardır . Gibi bir şey yazmalısın

import static org.mockito.Mockito.when;
import static org.mockito.AdditionalAnswers.returnsFirstArg;

when(myMock.myFunction(anyString())).then(returnsFirstArg());

Veya alternatif olarak

doAnswer(returnsFirstArg()).when(myMock).myFunction(anyString());

returnsFirstArg()Yöntemin AdditionalAnswersMockito 1.9.5 için yeni olan sınıfta statik olduğunu unutmayın ; böylece doğru statik içe aktarmaya ihtiyacınız olacaktır.


17
Not: when(...).then(returnsFirstArg())Yanlışlıkla when(...).thenReturn(returnsFirstArg())verdijava.lang.ClassCastException: org.mockito.internal.stubbing.answers.ReturnsArgumentAt cannot be cast to
Benedikt Köppel

1
Not: returnFirstArg (), bağımsız değişkenin değeri yerine Answer <> değerini döndürür. .ThenReturn (new Foo (returnFirstArg ()))
Lu55

Ben her zaman "Ek Cevaplar" hatırlayamıyorum ve ben çok nadiren ihtiyacım var gibi, bu cevap geçmiş yıllar için tekrar tekrar google gerekir. Sonra gerekli bağımlılıkları bulamadığım için bu senaryoyu nasıl oluşturabileceğimi merak ediyorum. Bu doğrudan mockito'ya eklenemez mi? : /
BAERUS

2
Steve'in cevabı daha genel. Bu sadece ham argümanı döndürmenize izin verir. Bu argümanı işlemek ve sonucu döndürmek istiyorsanız, Steve'in cevap kuralları. İkisi de faydalı olduğu için ikisini de oyladım.
akostadinov

Bilginize, ithal etmek zorundayız static org.mockito.AdditionalAnswers.returnsFirstArg. returnFirstArg kullanmak için bunu. Ayrıca, when(myMock.myFunction(any())).then(returnsFirstArg())Mockito 2.20 de yapabilirim . *
gtiwari333

78

Java 8 ile Mockito'nun eski sürümüyle bile tek satırlık bir yanıt oluşturmak mümkündür:

when(myMock.myFunction(anyString()).then(i -> i.getArgumentAt(0, String.class));

Tabii ki bu, AdditionalAnswersDavid Wallace'ın önerdiği kadar kullanışlı değil , ancak "anında" argümanını dönüştürmek istiyorsanız yararlı olabilir.


1
Parlak. Teşekkür ederim. Argüman ise long, bu hala boks ve ile çalışabilir Long.classmi?
vikingsteve

1
.getArgumentAt (..) benim için bulunamadı ancak .getArgument (1) çalıştı (mockito 2.6.2)
Curtis Yallop

41

Çok benzer bir sorun yaşadım. Amaç, Nesneleri sürdürebilen ve isimleriyle geri gönderebilen bir hizmeti taklit etmekti. Hizmet şöyle görünür:

public class RoomService {
    public Room findByName(String roomName) {...}
    public void persist(Room room) {...}
}

Hizmet sahte, Oda örneklerini saklamak için bir harita kullanır.

RoomService roomService = mock(RoomService.class);
final Map<String, Room> roomMap = new HashMap<String, Room>();

// mock for method persist
doAnswer(new Answer<Void>() {
    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        Object[] arguments = invocation.getArguments();
        if (arguments != null && arguments.length > 0 && arguments[0] != null) {
            Room room = (Room) arguments[0];
            roomMap.put(room.getName(), room);
        }
        return null;
    }
}).when(roomService).persist(any(Room.class));

// mock for method findByName
when(roomService.findByName(anyString())).thenAnswer(new Answer<Room>() {
    @Override
    public Room answer(InvocationOnMock invocation) throws Throwable {
        Object[] arguments = invocation.getArguments();
        if (arguments != null && arguments.length > 0 && arguments[0] != null) {
            String key = (String) arguments[0];
            if (roomMap.containsKey(key)) {
                return roomMap.get(key);
            }
        }
        return null;
    }
});

Şimdi bu sahte testlerimizi yapabiliriz. Örneğin:

String name = "room";
Room room = new Room(name);
roomService.persist(room);
assertThat(roomService.findByName(name), equalTo(room));
assertNull(roomService.findByName("none"));

34

Java 8 ile Steve'in cevabı olabilir

public void testMyFunction() throws Exception {
    Application mock = mock(Application.class);
    when(mock.myFunction(anyString())).thenAnswer(
    invocation -> {
        Object[] args = invocation.getArguments();
        return args[0];
    });

    assertEquals("someString", mock.myFunction("someString"));
    assertEquals("anotherString", mock.myFunction("anotherString"));
}

EDIT: Daha da kısa:

public void testMyFunction() throws Exception {
    Application mock = mock(Application.class);
    when(mock.myFunction(anyString())).thenAnswer(
        invocation -> invocation.getArgument(0));

    assertEquals("someString", mock.myFunction("someString"));
    assertEquals("anotherString", mock.myFunction("anotherString"));
}

6

Bu oldukça eski bir soru ama yine de alakalı olduğunu düşünüyorum. Ayrıca kabul edilen cevap sadece String için çalışır. Bu arada Mockito 2.1 var ve bazı ithalatlar değişti, bu yüzden mevcut cevabımı paylaşmak istiyorum:

import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@Mock
private MyClass myClass;

// this will return anything you pass, but it's pretty unrealistic
when(myClass.myFunction(any())).then(returnsFirstArg());
// it is more "life-like" to accept only the right type
when(myClass.myFunction(any(ClassOfArgument.class))).then(returnsFirstArg());

MyClass.myFunction şöyle görünecektir:

public class MyClass {
    public ClassOfArgument myFunction(ClassOfArgument argument){
        return argument;
    }  
}

4

Benzer bir şey kullanıyorum (temelde aynı yaklaşım). Bazen, sahte giriş nesnesinin belirli girişler için önceden tanımlanmış bir çıktı döndürmesi yararlı olur. Bu şöyle:

private Hashtable<InputObject,  OutputObject> table = new Hashtable<InputObject, OutputObject>();
table.put(input1, ouput1);
table.put(input2, ouput2);

...

when(mockObject.method(any(InputObject.class))).thenAnswer(
       new Answer<OutputObject>()
       {
           @Override
           public OutputObject answer(final InvocationOnMock invocation) throws Throwable
           {
               InputObject input = (InputObject) invocation.getArguments()[0];
               if (table.containsKey(input))
               {
                   return table.get(input);
               }
               else
               {
                   return null; // alternatively, you could throw an exception
               }
           }
       }
       );

4

Testte yürütmeyi sağlamak için verify () öğesini ArgumentCaptor ile ve argümanları değerlendirmek için ArgumentCaptor ile birlikte kullanmak isteyebilirsiniz:

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
verify(mock).myFunction(argument.capture());
assertEquals("the expected value here", argument.getValue());

Argümanın değerine, daha fazla manipülasyon / kontrol / ne olursa olsun argument.getValue () aracılığıyla açıkça erişilebilir.


3

Bu biraz eski, ama buraya geldim çünkü aynı sorunu yaşadım. JUnit kullanıyorum ama bu sefer mockk ile bir Kotlin uygulamasında. Burada Java muadili ile referans ve karşılaştırma için bir örnek gönderiyorum:

@Test
fun demo() {
  // mock a sample function
  val aMock: (String) -> (String) = mockk()

  // make it return the same as the argument on every invocation
  every {
    aMock.invoke(any())
  } answers {
    firstArg()
  }

  // test it
  assertEquals("senko", aMock.invoke("senko"))
  assertEquals("senko1", aMock.invoke("senko1"))
  assertNotEquals("not a senko", aMock.invoke("senko"))
}

2

Bunu ArgumentCaptor kullanarak yapabilirsiniz

Fasulye fonksiyonuna sahip olduğunuzu düşünün.

public interface Application {
  public String myFunction(String abc);
}

Sonra test sınıfınızda:

//Use ArgumentCaptor to capture the value
ArgumentCaptor<String> param = ArgumentCaptor.forClass(String.class);


when(mock.myFunction(param.capture())).thenAnswer(new Answer<String>() {
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
      return param.getValue();//return the captured value.
    }
  });

VEYA lambda hayranıysanız yapmanız gerekenler:

//Use ArgumentCaptor to capture the value
ArgumentCaptor<String> param = ArgumentCaptor.forClass(String.class);


when(mock.myFunction(param.capture()))
    .thenAnswer((invocation) -> param.getValue());

Özet: Geçirilen parametreyi yakalamak için argumentcaptor kullanın. Daha sonra yanıt olarak getValue kullanılarak yakalanan değeri döndürün.


Bu işe yaramıyor (artık?). Dokümanlarla ilgili olarak: Bu yöntem doğrulama içinde kullanılmalıdır. Bu, yalnızca doğrulama yöntemini kullanırken değeri yakalayabileceğiniz anlamına gelir
Muhammed Misir

1. Ne demek istediğinizden emin değilim This doesn´t work (anymore?).. 2. Üzgünüm, yapmaya çalıştığınız noktada net değilim. Cevap OP'nin sorusuna özeldir.
Cyril Cherian
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.