Mockito ile Spring'de otomatik kablolu bir @Value alanıyla nasıl dalga geçebilirim?


125

Spring 3.1.4.RELEASE ve Mockito 1.9.5 kullanıyorum. Bahar dersimde:

@Value("#{myProps['default.url']}")
private String defaultUrl;

@Value("#{myProps['default.password']}")
private String defaultrPassword;

// ...

Şu anda bu şekilde kurduğum JUnit testimden:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest 
{ 

"DefaultUrl" alanım için bir değerle alay etmek istiyorum. Diğer alanlar için değerlerle alay etmek istemediğime dikkat edin - bunları oldukları gibi tutmak istiyorum, sadece "defaultUrl" alanı. Ayrıca, setDefaultUrlsınıfımda açık bir "ayarlayıcı" yöntemim olmadığını (örn. ) Ve yalnızca test amacıyla herhangi bir yöntem oluşturmak istemediğimi unutmayın.

Bu göz önüne alındığında, bu tek alan için bir değerle nasıl dalga geçebilirim?

Yanıtlar:


145

ReflectionTestUtils.setFieldKodunuzda herhangi bir değişiklik yapmaktan kaçınmak için Spring'in büyüsünü kullanabilirsiniz .

Yöntemin kullanımı çok kolay olduğu için muhtemelen ihtiyacınız olmayacak olsa da, daha fazla bilgi için bu eğiticiye göz atın.

GÜNCELLEME

Bahar 4.2.RC1'in tanıtılmasından bu yana, sınıfın bir örneğini sağlamak zorunda kalmadan statik bir alan ayarlamak artık mümkün. Bkz bu belgelerin bir kısmını ve bu taahhüt.


13
Sadece bağlantının kesilmesi durumunda: test sırasında yönteminizi ReflectionTestUtils.setField(bean, "fieldName", "value");çağırmadan önce kullanın bean.
Michal Stochmal

2
Özellikler dosyasından alınan özelliklerle alay etmek için iyi bir çözüm.
Antony Sampath Kumar Reddy

@ MichałStochmal, dosyalandığı için bunu yapmak özel java.lang.IllegalStateException: yönteme erişilemedi: Sınıf org.springframework.util.ReflectionUtils, değiştiricilerle com.kaleidofin.app.service.impl.CVLKRAProvider sınıfının bir üyesine erişemez "" org.springframework.util.ReflectionUtils.handleReflectionException (ReflectionUtils.java:112) at org.springframework.util.ReflectionUtils.setField (ReflectionUtils.java:655)
Akhil Surapuram

113

Bir @ Değer alanını nasıl alay edeceğimi her zaman unuttuğumdan, bu SO gönderisine üçüncü kez gitmiştim. Kabul edilen cevap doğru olsa da, "setField" çağrısını doğru yapmak için her zaman biraz zamana ihtiyacım var, bu yüzden en azından kendim için buraya bir örnek pasajı yapıştırıyorum:

Üretim sınıfı:

@Value("#{myProps[‘some.default.url']}")
private String defaultUrl;

Test sınıfı:

import org.springframework.test.util.ReflectionTestUtils;

ReflectionTestUtils.setField(instanceUnderTest, "defaultUrl", "http://foo");
// Note: Don't use MyClassUnderTest.class, use the instance you are testing itself
// Note: Don't use the referenced string "#{myProps[‘some.default.url']}", 
//       but simply the FIELDs name ("defaultUrl")

32

Ayrıca mülk yapılandırmanızı test sınıfınıza taklit edebilirsiniz.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest 
{ 
   @Configuration
   public static class MockConfig{
       @Bean
       public Properties myProps(){
             Properties properties = new Properties();
             properties.setProperty("default.url", "myUrl");
             properties.setProperty("property.value2", "value2");
             return properties;
        }
   }
   @Value("#{myProps['default.url']}")
   private String defaultUrl;

   @Test
   public void testValue(){
       Assert.assertEquals("myUrl", defaultUrl);
   }
}

26

Sınıfı @Valuekullanmak yerine yapıcıya parametreler olarak açıklamalı alanları geçirmek olan ilgili bir çözüm önermek istiyorum ReflectionTestUtils.

Bunun yerine:

public class Foo {

    @Value("${foo}")
    private String foo;
}

ve

public class FooTest {

    @InjectMocks
    private Foo foo;

    @Before
    public void setUp() {
        ReflectionTestUtils.setField(Foo.class, "foo", "foo");
    }

    @Test
    public void testFoo() {
        // stuff
    }
}

Bunu yap:

public class Foo {

    private String foo;

    public Foo(@Value("${foo}") String foo) {
        this.foo = foo;
    }
}

ve

public class FooTest {

    private Foo foo;

    @Before
    public void setUp() {
        foo = new Foo("foo");
    }

    @Test
    public void testFoo() {
        // stuff
    }
}

Bu yaklaşımın faydaları: 1) Foo sınıfını bir bağımlılık konteyneri olmadan başlatabiliriz (bu sadece bir yapıcıdır) ve 2) testimizi uygulama ayrıntılarımıza bağlamıyoruz (yansıma bizi bir dize kullanarak alan adına bağlar, alan adını değiştirirsek soruna neden olabilir).


3
olumsuz: Birisi notla uğraşıyorsa, örneğin 'foo' yerine bir özellik 'bar' kullanıyorsa, testiniz yine de çalışacaktır. Sadece bu davam var.
Nils El-Himoud

@ NilsEl-Himoud Bu, genel olarak OP sorusu için adil bir nokta, ancak ortaya koyduğunuz konu, yansıtma araçlarını kullanarak yapıcıya karşı daha iyi veya daha kötü bir durum değil. Bu cevabın amacı, yapılandırıcıyı yansıtma yararına göre değerlendirmekti (kabul edilen cevap). Mark, cevap için teşekkürler, bu ince ayarın kolaylığını ve temizliğini takdir ediyorum.
Marquee

22

Bu sihirli Bahar Testi açıklamasını kullanabilirsiniz:

@TestPropertySource(properties = { "my.spring.property=20" }) 

bkz. org.springframework.test.context.TestPropertySource

Örneğin, bu test sınıfıdır:

@ContextConfiguration(classes = { MyTestClass.Config.class })
@TestPropertySource(properties = { "my.spring.property=20" })
public class MyTestClass {

  public static class Config {
    @Bean
    MyClass getMyClass() {
      return new MyClass ();
    }
  }

  @Resource
  private MyClass myClass ;

  @Test
  public void myTest() {
   ...

Ve bu, özelliğe sahip sınıftır:

@Component
public class MyClass {

  @Value("${my.spring.property}")
  private int mySpringProperty;
   ...


1

Ayrıca sınıfımda açık bir "ayarlayıcı" yöntemim (örn. SetDefaultUrl) olmadığına ve yalnızca test amacıyla herhangi bir yöntem oluşturmak istemediğime dikkat edin.

Bunu çözmenin bir yolu , test ve Yay enjeksiyonu için kullanılan Yapıcı Enjeksiyonu kullanmak için sınıfınızı değiştirmektir . Daha fazla yansıma yok :)

Böylece, yapıcıyı kullanarak herhangi bir String geçirebilirsiniz:

class MySpringClass {

    private final String defaultUrl;
    private final String defaultrPassword;

    public MySpringClass (
         @Value("#{myProps['default.url']}") String defaultUrl, 
         @Value("#{myProps['default.password']}") String defaultrPassword) {
        this.defaultUrl = defaultUrl;
        this.defaultrPassword= defaultrPassword;
    }

}

Ve testinizde sadece kullanın:

MySpringClass MySpringClass  = new MySpringClass("anyUrl", "anyPassword");
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.