2013-06-27 16 views
105

Ich versuche, einen Komponententest für eine einfache Bean zu schreiben, die in meinem Programm verwendet wird, um Formulare zu validieren. Die Bohne ist mit @Component kommentiert und hat eine Klassenvariable, die initialisiert wird mit @Value("${this.property.value}") private String thisProperty;Füllen der Quelle @Wert während des Komponententests

Ich mag Unit-Tests für die Validierungsmethoden innerhalb dieser Klasse schreiben, aber, wenn möglich, ich möchte, dies zu tun, ohne die Eigenschaften unter Verwendung von Datei . Meine Überlegungen dahinter sind, dass sich der Wert, den ich aus der Eigenschaftendatei gezogen habe, nicht auf meinen Testfall auswirkt. Mein Testfall testet den Code, der den Wert überprüft, nicht den Wert selbst.

Gibt es eine Möglichkeit, Java-Code innerhalb meiner Testklasse zu verwenden, um eine Java-Klasse zu initialisieren und die Spring @ Value-Eigenschaft in dieser Klasse zu füllen und sie dann zum Testen zu verwenden?

Ich habe diese How To gefunden, die in der Nähe zu sein scheint, aber immer noch eine Eigenschaftendatei verwendet. Ich würde lieber alles Java-Code sein.

Dank

Antwort

88

Wenn möglich, ich würde versuchen, diese Tests ohne Frühling Kontext zu schreiben. Wenn Sie diese Klasse in Ihrem Test ohne Feder erstellen, haben Sie die volle Kontrolle über ihre Felder.

Um das @value Feld zu setzen, können Sie Springs ReflectionTestUtils verwenden - es hat eine Methode setField private Felder zu setzen.

@see JavaDoc: ReflectionTestUtils.setField(java.lang.Object, java.lang.String, java.lang.Object)

+1

Genau das, was ich versuche, zu tun und was ich suchte den Wert in meiner Klasse zu setzen, danke! – Kyle

+1

Oder sogar ohne Spring-Abhängigkeiten, indem das Feld auf den Standardzugriff geändert wird (Paket geschützt), um es für den Test einfach zugänglich zu machen. –

+2

Beispiel: 'org.springframework.test.util.ReflectionTestUtils.setField (classUnderTest," Feld "," Wert ");' – Olivier

36

Wenn Sie möchten, können Sie immer noch Ihre Tests im Frühjahr Kontext ausführen und die gewünschten Eigenschaften innerhalb Spring-Konfigurations Klasse. Wenn Sie JUnit verwenden, verwenden SpringJUnit4ClassRunner und spezielle Konfigurationsklasse definieren für Ihre Tests wie folgt aus:

Die Klasse im Test:

@Component 
public SomeClass { 

    @Autowired 
    private SomeDependency someDependency; 

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

Die Testklasse:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = SomeClassTestsConfig.class) 
public class SomeClassTests { 

    @Autowired 
    private SomeClass someClass; 

    @Autowired 
    private SomeDependency someDependency; 

    @Before 
    public void setup() { 
     Mockito.reset(someDependency); 

    @Test 
    public void someTest() { ... } 
} 

Und die Konfigurationsklasse für dieser Test:

@Configuration 
public class SomeClassTestsConfig { 

    @Bean 
    public static PropertySourcesPlaceholderConfigurer properties() throws Exception { 
     final PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer(); 
     Properties properties = new Properties(); 

     properties.setProperty("someProperty", "testValue"); 

     pspc.setProperties(properties); 
     return pspc; 
    } 
    @Bean 
    public SomeClass getSomeClass() { 
     return new SomeClass(); 
    } 

    @Bean 
    public SomeDependency getSomeDependency() { 
     // Mockito used here for mocking dependency 
     return Mockito.mock(SomeDependency.class); 
    } 
} 

Nachdem das gesagt ist, würde ich nicht empfehlen Dieser Ansatz habe ich hier nur als Referenz hinzugefügt. Meiner Meinung nach ist der Mockito-Läufer der bessere Weg. In diesem Fall führen Sie keine Tests innerhalb von Spring durch, was viel klarer und einfacher ist.

+2

Ich stimme zu, dass die meisten Logik mit Mockito getestet werden sollte. Ich wünschte, es gäbe eine bessere Möglichkeit, Präsenz und Korrektheit von Anmerkungen zu testen, als Tests über Spring auszuführen. – Altair7852

15

Dies scheint zu stehen, ausführliche ein bisschen obwohl noch (ich etwas kürzer noch möchte):

@BeforeClass 
public static void beforeClass() { 
    System.setProperty("some.property", "<value>"); 
} 

// Optionally: 
@AfterClass 
public static void afterClass() { 
    System.clearProperty("some.property"); 
} 
+0

Ich denke, dass diese Antwort sauberer ist, da sie vom Frühling unabhängig ist, sie funktioniert gut für verschiedene Szenarien, wie wenn Sie benutzerdefinierte Testläufer verwenden müssen und nicht einfach die '@ TestProperty' Annotation hinzufügen können. – raspacorp

4

PropertyPlaceholderConfigurer in Konfiguration Hinzufügen arbeitet für mich.

@Configuration 
@ComponentScan 
@EnableJpaRepositories 
@EnableTransactionManagement 
public class TestConfiguration { 
@Bean 
public DataSource dataSource() { 
    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); 
    builder.setType(EmbeddedDatabaseType.DERBY); 
    return builder.build(); 
} 

@Bean 
public LocalContainerEntityManagerFactoryBean entityManagerFactory() { 
    LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); 
    entityManagerFactoryBean.setDataSource(dataSource()); 
    entityManagerFactoryBean.setPackagesToScan(new String[] { "com.test.model" }); 
    // Use hibernate 
    JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 
    entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter); 
    entityManagerFactoryBean.setJpaProperties(getHibernateProperties()); 
    return entityManagerFactoryBean; 
} 

private Properties getHibernateProperties() { 
    Properties properties = new Properties(); 
    properties.put("hibernate.show_sql", "false"); 
    properties.put("hibernate.dialect", "org.hibernate.dialect.DerbyDialect"); 
    properties.put("hibernate.hbm2ddl.auto", "update"); 
    return properties; 
} 

@Bean 
public JpaTransactionManager transactionManager() { 
    JpaTransactionManager transactionManager = new JpaTransactionManager(); 
    transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); 
    return transactionManager; 
} 

@Bean 
PropertyPlaceholderConfigurer propConfig() { 
    PropertyPlaceholderConfigurer placeholderConfigurer = new PropertyPlaceholderConfigurer(); 
    placeholderConfigurer.setLocation(new ClassPathResource("application_test.properties")); 
    return placeholderConfigurer; 
} 

}

Und in Testklasse

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = TestConfiguration.class) 
public class DataServiceTest { 

@Autowired 
private DataService dataService; 

@Autowired 
private DataRepository dataRepository; 

@Value("${Api.url}") 
private String baseUrl; 

@Test 
public void testUpdateData() { 
    List<Data> datas = (List<Data>) dataRepository.findAll(); 
    assertTrue(datas.isEmpty()); 
    dataService.updateDatas(); 
    datas = (List<Data>) dataRepository.findAll(); 
    assertFalse(datas.isEmpty()); 
} 

}

57

Seit Spring 4.1 Sie könnten Eigenschaftswerte nur im Code festlegen, indem Sie die Annotation org.springframework.test.context.TestPropertySource auf der Klassenebene Unit Tests verwenden. Man könnte diesen Ansatz auch für Eigenschaften in abhängige Bean-Instanzen

Zum Beispiel

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = FooTest.Config.class) 
@TestPropertySource(properties = { 
    "some.bar.value=testValue", 
}) 
public class FooTest { 

    @Value("${some.bar.value}") 
    String bar; 

    @Test 
    public void testValueSetup() { 
    assertEquals("testValue", bar); 
    } 


    @Configuration 
    static class Config { 

    @Bean 
    public static PropertySourcesPlaceholderConfigurer propertiesResolver() { 
     return new PropertySourcesPlaceholderConfigurer(); 
    } 

    } 

} 

Hinweis Injektion: Es ist notwendig Instanz org.springframework.context.support.PropertySourcesPlaceholderConfigurer im Frühjahr Kontext haben

bearbeiten 24-08- 2017: Wenn Sie SpringBoot 1.4.0 und höher verwenden, können Sie Tests mit @SpringBootTest und @SpringBootConfiguration Annotationen initialisieren. Mehr Infos here

Bei SpringBoot haben wir folgenden Code

@SpringBootTest 
@SpringBootConfiguration 
@RunWith(SpringJUnit4ClassRunner.class) 
@TestPropertySource(properties = { 
    "some.bar.value=testValue", 
}) 
public class FooTest { 

    @Value("${some.bar.value}") 
    String bar; 

    @Test 
    public void testValueSetup() { 
    assertEquals("testValue", bar); 
    } 

} 
+0

Danke, endlich hat jemand geantwortet, wie Value überschrieben wird und nicht, wie man ein Feld einstellt. Ich leite Werte aus dem String-Feld in PostConstruct ab, und daher brauche ich den String-Wert, der von Spring gesetzt werden soll, nicht nach der Konstruktion. – tequilacat

+0

Ich bin froh zu helfen –

Verwandte Themen