2014-04-18 13 views
44

Sto usando Spring 3.1.4.RELEASE e Mockito 1.9.5. Nella mia classe primavera ho:Come faccio a prendere in giro un campo @Value autowired in primavera con Mockito?

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

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

// ... 

Dal mio test JUnit, che io attualmente ho impostato in questo modo:

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

vorrei prendere in giro un valore per il mio campo "defaultUrl". Si noti che non voglio simulare valori per gli altri campi: mi piacerebbe mantenerli così come sono, solo il campo "defaultUrl". Nota anche che non ho metodi "setter" espliciti (ad esempio setDefaultUrl) nella mia classe e non desidero crearne uno solo ai fini del test.

Dato questo, come posso prendere in giro un valore per quell'unico campo?

risposta

73

È possibile utilizzare la magia di Spring ReflectionTestUtils.setField per evitare di apportare modifiche di sorta al codice.

Partenza this tutorial per ancora più informazioni, anche se probabilmente non sarà necessario da quando il metodo è molto facile da usare

UPDATE

Dall'introduzione della primavera 4.2.RC1 è ora è possibile impostare un campo statico senza dover fornire un'istanza della classe. Vedere this parte della documentazione e commit this.

22

Si può anche prendere in giro la configurazione di proprietà nella vostra classe di test

@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); 
    } 
} 
+1

+1 Questo approccio verifica effettivamente @Value. – MarkOfHall

31

Era la terza volta che mi googled a questo SO posto come mi dimentico sempre come per deridere un campo @Value. Anche se la risposta accettata è corretta, ho sempre bisogno di tempo per ottenere il "setField" chiamata a destra, così almeno per me incollo un frammento di esempio qui:

Dato:

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

Nel metodo di prova (nome qualificato solo per completezza qui):

org.springframework.test.util.ReflectionTestUtils.setField(
     myClassUnderTest, "defaultUrl", "url123"); 
// Note: Don't use MyClassUnderTest.class 
// Note: Don't use the referenced string "#{myProps[‘default.url']}", 
//  but simply the field name "defaultUrl" 
1

vorrei suggerire una soluzione correlata, che è quello di superare i @Value campi -annotated come parametri al costruttore, invece di utilizzare la classe ReflectionTestUtils.

Invece di questo:

public class Foo { 

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

e

public class FooTest { 

    @InjectMocks 
    private Foo foo; 

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

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

fare questo:

public class Foo { 

    private String foo; 

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

e

public class FooTest { 

    private Foo foo; 

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

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

Benefici di questo approccio: 1) possiamo istanziare la classe Foo senza un contenitore di dipendenze (è solo un costruttore), e 2) non accoppiamo il nostro test ai nostri dettagli di implementazione (la riflessione ci lega al nome del campo usando una stringa , che potrebbe causare un problema se cambiamo il nome del campo).