Mockito: Gerçek nesneleri özel @ Otomatik alanlara enjekte edin


191

Mockito'nun @Mockve @InjectMocksek açıklamalarını, Spring'in ek açıklamalarıyla eklenmiş özel alanlara enjekte etmek için kullanıyorum @Autowired:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Mock
    private SomeService service;

    @InjectMocks
    private Demo demo;

    /* ... */
}

ve

public class Demo {

    @Autowired
    private SomeService service;

    /* ... */
}

Şimdi özel alanlara (ayarlayıcı olmadan) gerçek nesneler de enjekte etmek istiyorum @Autowired. Bu mümkün mü yoksa mekanizma sadece Mock enjekte etmekle mi sınırlı?


5
Normalde bir şeylerle alay ederken, somut nesneyi fazla önemsemediğiniz anlamına gelir; sadece alay konusu nesnenin davranışını önemsediğinizi Bunun yerine bir entegrasyon testi yapmak istersiniz? Ya da, neden alaycı ve somut nesnelerin birlikte yaşamasını istediğinize dair bir gerekçe sunabilir misiniz?
Makoto

2
Eh, eski kod ile ilgileniyorum ve ne zaman (...). ThenReturn (...) ifadeleri sadece bazı NPE ve benzeri önlemek için sahte kurmak için çok zaman alacaktır. Öte yandan, gerçek bir nesne bunun için güvenle kullanılabilir. Bu nedenle, alaylarla birlikte gerçek nesneleri enjekte etme seçeneğine sahip olmak çok kullanışlı olacaktır. Bu bir kod kokusu olsa bile, bu özel durumda makul olduğunu düşünüyorum.
user2286693

Unutma MockitoAnnotations.initMocks(this);içinde @Beforeyöntemle. Bunun orijinal soru ile doğrudan ilişkili olmadığını biliyorum, ancak daha sonra gelen herkes için, bu çalışılabilir hale getirmek için eklenmesi gerekir.
Cuga

7
@Cuga: JUnit için Mockito koşucusunu kullanırsanız, ( @RunWith(MockitoJUnitRunner.class)), hatta ihtiyacınız yokturMockitoAnnotations.initMocks(this);
Clint Eastwood

1
Teşekkürler-- Bunu hiç bilmiyordum ve her zaman ikisini de
belirtiyordum

Yanıtlar:


306

@SpyEk açıklama kullan

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Spy
    private SomeService service = new RealServiceImpl();

    @InjectMocks
    private Demo demo;

    /* ... */
}

Mockito, ek açıklama içeren @Mockveya @Spyek açıklama içeren tüm alanları ek açıklama eklenmiş örneğe enjekte edilecek potansiyel adaylar olarak değerlendirecektir @InjectMocks. Yukarıdaki durumda, 'RealServiceImpl'örnek 'demoya' enjekte edilecektir

Daha fazla ayrıntı için bkz.

Mockito-ev

@Casus

@Mock


9
+1: Benim için çalıştı ... String nesneleri hariç. Mockito şikayet ediyor:Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types
Adrian Pronk

Teşekkürler Benim için de çalıştı :) Gerçek uygulama kullanımı için sahte başka proxy kullanımı yapmak için casus
Swarit Agarwal

Teşekkürler! Tam da ihtiyacım olan şey bu!
nterry

2
Benim durumumda, Mockito bir Casus enjekte etmiyor . Yine de bir sahte enjekte eder. Alan özel ve ayarlayıcı içermiyor.
Vituel

8
BTW, gerek yok new RealServiceImpl(), @Spy private SomeService service;yine de üzerinde casusluk yapmadan önce varsayılan yapıcı kullanarak gerçek bir nesne oluşturur.
parxier

20

@Dev Blanked cevabına ek olarak, Spring tarafından oluşturulan mevcut bir fasulyeyi kullanmak istiyorsanız kod şu şekilde değiştirilebilir:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {

    @Inject
    private ApplicationContext ctx;

    @Spy
    private SomeService service;

    @InjectMocks
    private Demo demo;

    @Before
    public void setUp(){
        service = ctx.getBean(SomeService.class);
    }

    /* ... */
}

Bu şekilde, sadece testlerin çalışması için kodunuzu değiştirmeniz gerekmez (başka bir kurucu ekleyin).


2
@Aada Ayrıntılı olabilir misiniz?
Yoaz Menda

1
Bu hangi kütüphaneden geliyor, sadece org.mockito'da InjectMocks'u görebiliyorum
Sameer

1
@sameer import org.mockito.InjectMocks;
Yoaz Menda

Aada's iptal etmek için yorum ekleme. Bu benim için çalıştı. Diğer cevapların önerdiği gibi "sadece alan enjeksiyonunu kullanamadım", bu yüzden Autowiredbağımlılık alanlarının doğru oluşturulduğundan emin olmak zorunda kaldım .
Scrambo

1
Bunu denedim. ama kurulum yönteminden boş işaretçi istisnası
alıyorum

3

Mockito bir DI çerçevesi değildir ve hatta DI çerçeveleri bile, alan enjeksiyonları üzerine yapıcı enjeksiyonlarını teşvik eder.
Yani sadece bir yapıcıyı test edilen sınıfın bağımlılıklarını ayarladığını bildirirsiniz:

@Mock
private SomeService serviceMock;

private Demo demo;

/* ... */
@BeforeEach
public void beforeEach(){
   demo = new Demo(serviceMock);
}

spyGenel durum için Mockito kullanmak korkunç bir tavsiye. Test sınıfını kırılgan, düz ve hataya yatkın hale getirir: Gerçekten alay konusu olan nedir? Gerçekten test edilen nedir?
@InjectMocksve @Spyo sınıflarında şişirilmiş sınıfları ve karışık sorumluluklarını teşvik beri de genel tasarım acıyor. Bunu körü körüne kullanmadan önce
lütfen javadoc'u okuyunspy() (vurgu benim değildir):

Gerçek nesnenin bir casusunu oluşturur. Casus , saplanmadığı sürece gerçek yöntemleri çağırır . Gerçek casuslar , örneğin eski kodlarla uğraşırken dikkatli ve zaman zaman kullanılmalıdır .

Her zamanki gibi şunları okuyacaksınız partial mock warning: Nesne yönelimli programlama, karmaşıklığı ayrı, spesifik, SRPy nesnelerine bölerek karmaşıklığı ele alır. Kısmi alay bu paradigmaya nasıl uyuyor? Eh, sadece ... Kısmi alay genellikle karmaşıklığın aynı nesne üzerinde farklı bir yönteme taşındığı anlamına gelir. Çoğu durumda, uygulamanızı tasarlamak istediğiniz yol bu değildir.

Ancak, kısmi alayların işe yaradığı nadir durumlar vardır: kodla uğraşmak kolay bir şekilde değiştiremezsiniz (3. taraf arayüzler, eski kodun yeniden düzenlenmesi vb.) Ancak, yeni, test odaklı ve iyi- tasarlanmış kod.


0

İlkbaharda ReflectionTestUtilsbu amaç için özel bir yardımcı program var . Belirli örneği alın ve alana enjekte edin.


@Spy
..
@Mock
..

@InjectMock
Foo foo;

@BeforeEach
void _before(){
   ReflectionTestUtils.setField(foo,"bar", new BarImpl());// `bar` is private field
}

-1

Bunun eski bir soru olduğunu biliyorum, ama Dizeleri enjekte etmeye çalışırken aynı sorunla karşı karşıya kaldık. Bu yüzden tam olarak ne istediğinizi yapan bir JUnit5 / Mockito uzantısı icat ettik: https://github.com/exabrial/mockito-object-injection

DÜZENLE:

@InjectionMap
 private Map<String, Object> injectionMap = new HashMap<>();

 @BeforeEach
 public void beforeEach() throws Exception {
  injectionMap.put("securityEnabled", Boolean.TRUE);
 }

 @AfterEach
 public void afterEach() throws Exception {
  injectionMap.clear();
 }
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.