J'utilise Spring 3.1.4.RELEASE et Mockito 1.9.5. Dans ma classe de printemps, j'ai:
@Value("#{myProps['default.url']}")
private String defaultUrl;
@Value("#{myProps['default.password']}")
private String defaultrPassword;
// ...
De mon test JUnit, que j'ai actuellement configuré comme suit:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
Je voudrais me moquer d'une valeur pour mon champ "defaultUrl". Notez que je ne veux pas me moquer des valeurs pour les autres champs - j'aimerais garder celles-ci telles qu'elles sont, seul le champ "defaultUrl". Notez également que je n’ai pas de méthode "setter" explicite (par exemple, setDefaultUrl
) dans ma classe et que je ne souhaite pas en créer pour les besoins du test.
Compte tenu de cela, comment puis-je simuler une valeur pour ce champ?
Vous pouvez utiliser la magie de ReflectionTestUtils.setField
de Spring afin d'éviter toute modification de votre code.
Consultez le tutoriel this pour plus d'informations, bien que vous n'en ayez probablement pas besoin, car cette méthode est très facile à utiliser
UPDATE
Depuis l'introduction de Spring 4.2.RC1, il est maintenant possible de définir un champ statique sans avoir à fournir une instance de la classe. Voir this une partie de la documentation et this commit.
C’était la troisième fois que je me cherchais moi-même dans ce post SO, car j’ai toujours oublié comment me moquer d’un champ @Value. Bien que la réponse acceptée soit correcte, il me faut toujours un peu de temps pour que l'appel "setField" soit correct. C'est pourquoi, au moins pour moi, je colle un extrait de code ici:
Classe de production:
@Value("#{myProps[‘some.default.url']}")
private String defaultUrl;
Classe de test:
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")
Vous pouvez également simuler la configuration de votre propriété dans votre classe de 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);
}
}
J'aimerais suggérer une solution connexe, qui consiste à transmettre les paramètres annotés @Value
- en tant que paramètres au constructeur, au lieu d'utiliser la classe ReflectionTestUtils
.
Au lieu de cela:
public class Foo {
@Value("${foo}")
private String foo;
}
et
public class FooTest {
@InjectMocks
private Foo foo;
@Before
public void setUp() {
ReflectionTestUtils.setField(Foo.class, "foo", "foo");
}
@Test
public void testFoo() {
// stuff
}
}
Faire ceci:
public class Foo {
private String foo;
public Foo(@Value("${foo}") String foo) {
this.foo = foo;
}
}
et
public class FooTest {
private Foo foo;
@Before
public void setUp() {
foo = new Foo("foo");
}
@Test
public void testFoo() {
// stuff
}
}
Les avantages de cette approche: 1) nous pouvons instancier la classe Foo sans conteneur de dépendances (c'est juste un constructeur), et 2) nous ne couplons pas notre test aux détails de notre implémentation (la réflexion nous lie au nom du champ à l'aide d'une chaîne, ce qui pourrait causer un problème si nous changeons le nom du champ).
Vous pouvez utiliser cette annotation magique Spring Test:
@TestPropertySource(properties = { "my.spring.property=20" })
voir org.springframework.test.context.TestPropertySource
Par exemple, voici la classe de test:
@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() {
...
Et voici la classe avec la propriété:
@Component
public class MyClass {
@Value("${my.spring.property}")
private int mySpringProperty;
...
Notez également que je n’ai pas de méthode "setter" explicite (par exemple, setDefaultUrl) dans ma classe et que je ne souhaite pas en créer pour les besoins du test.
Une façon de résoudre ce problème consiste à changer votre classe pour qu'elle utilise Injection de constructeur, utilisée pour les tests et l’injection de ressort. Pas plus de réflexion :)
Donc, vous pouvez passer n'importe quelle chaîne en utilisant le constructeur:
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;
}
}
Et dans votre test, utilisez-le:
MySpringClass MySpringClass = new MySpringClass("anyUrl", "anyPassword");
J'ai utilisé le code ci-dessous et cela a fonctionné pour moi:
@InjectMocks
private AccessFeatureActivationStrategy activationStrategy = new AccessFeatureActivationStrategy();
@Before
public void setUp() {
ReflectionTestUtils.setField(activationStrategy, "constantFromConfigFile", 3);
}
Référence: https://www.jeejava.com/mock-an-autowired-value-field-in-spring-with-junit-mockito/