2011-01-04 20 views
8

Poszukuję najlepszych praktyk dotyczących konfigurowania testów jednostkowych i integracyjnych za pomocą Spring.Sprężyna: testy jednostkowe i integracyjne

Zwykle używam 3 rodzaje testów:

  • „prawdziwych” testów jednostkowych (brak zależności)
  • testy wykonywane zarówno jako db „jednostka” test (w pamięci, połączeń lokalnych, makiety obiektów , ...) lub jako test integracji (persistent dB zdalnych połączeń, ...)
  • testy wykonywane jedynie jako testów integracyjnych

Obecnie mam tylko testy drugiego Categ ory, co jest trudną częścią. I set-up na bazie klasy testy takie jak:

@ContextConfiguration(locations = { "/my_spring_test.xml" }) 
public abstract class AbstractMyTestCase extends AbstractJUnit4SpringContextTests 

i "Unit" testy takie jak:

public class FooTest extends AbstractMyTestCase 

z autowired atrybutów.

Jaki jest najlepszy sposób na przeprowadzenie testu w innym środowisku (test integracji)? Podklasy test i zastąpić ContextConfiguration?

@ContextConfiguration(locations = { "/my_spring_integration_test.xml" }) 
public class FooIntegrationTest extends FooTest 

Czy ta praca (nie mogę obecnie łatwo przetestować tutaj)? Problem z tym podejściem polega na tym, że "@ContextConfiguration (locations = {" /my_spring_integration_test.xml "})" jest często duplikowany.

Wszelkie sugestie?

Pozdrawiam, Florian

+0

Czy znalazłeś rozwiązanie, które pasuje do Twojego? – FrVaBe

Odpowiedz

2

pójdę z tej wersji:

ContextConfiguration(locations = { "/my_spring_test.xml" }) 
public abstract class AbstractMyTestCase extends AbstractJUnit4SpringContextTests 

aw my_spring_test.xml, chciałbym skorzystać z mechanizmu PropertyPlaceHolderConfigurer.

Przykład JPA:

<context:property-placeholder 
    system-properties-mode="OVERRIDE" 
    location="classpath:test.properties" /> 

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
    destroy-method="close"> 
    <property name="driverClassName" value="${test.database.driver}" /> 
    <property name="url" value="${test.database.server}" /> 
    <property name="username" value="${test.database.user}" /> 
    <property name="password" value="${test.database.password}" /> 
</bean> 

<bean id="entityManagerFactory" 
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
    <property name="persistenceUnitName" value="test" /> 
    <property name="dataSource" ref="dataSource" /> 
    <property name="persistenceXmlLocation" 
      value="classpath:META-INF/persistence.xml" /> 
    <property name="jpaVendorAdapter"> 
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
    <property name="showSql" value="false" /> 
    <property name="generateDdl" value="${test.database.update}" /> 
    <property name="database" value="${test.database.databasetype}" /> 
</bean> 
    </property> 
</bean> 

Teraz wszystko, co musisz zrobić, to mieć różne wersje test.properties na ścieżce klasy dla in-memory i testy rzeczywistej integracji (i oczywiście odpowiednich klas sterownika potrzebne być obecnym). Można nawet ustawić właściwości systemu, aby nadpisać wartości właściwości.


Jeśli chcesz to przygotować z maven, przekonasz się, że kopiowanie plików za pomocą maven nie jest banalne. Będziesz potrzebował sposobu na wykonanie kodu, standardowymi opcjami są maven-antrun-plugin i gmaven-maven-plugin.

Tak czy inaczej: mają dwa pliki konfiguracyjne, np. w src/main/config i dodaj dwie wersje wtyczki, jedną w fazie generate-test-resources i jedną w fazie pre-integration-test.Oto wersja GMaven:

<plugin> 
    <groupId>org.codehaus.gmaven</groupId> 
    <artifactId>gmaven-plugin</artifactId> 
    <version>1.3</version> 
    <executions> 
     <execution> 
      <phase>pre-integration-test</phase> 
      <goals> 
       <goal>execute</goal> 
      </goals> 
      <configuration> 
      <source> 
      new File(
       pom.build.testOutputDirectory, 
       "test.properties" 
      ).text = new File(
         pom.basedir, 
         "src/main/config/int-test.properties" 
      ).text; 
      </source> 
      </configuration> 
     </execution> 
     <execution> 
      <phase>generate-test-resources</phase> 
      <goals> 
       <goal>execute</goal> 
      </goals> 
      <configuration> 
      <source> 
      new File(
       pom.build.testOutputDirectory, 
       "test.properties" 
      ).text = new File(
         pom.basedir, 
         "src/main/config/memory-test.properties" 
      ).text; 
      </source> 
      </configuration> 
     </execution> 
    </executions> 
</plugin> 
+0

Tak, używam już elementu zastępczego własności i zastanawiałem się nad zastąpieniem pliku w ścieżce klas. Jaki jest najłatwiejszy sposób na zrobienie tego z Mavenem? Oddzielne projekty/zależności, które zawierają pojedynczy plik właściwości, a następnie dostosowują zależność w jakiś sposób zależne od fazy/profilu? – Puce

+0

@Puce dodany kod maven –

+0

Dzięki, choć nie chcę wprowadzić Groovy do projektu w tym momencie. Myślę, że problem polega na tym, że Maven brakuje odpowiedniego wsparcia testu integracyjnego: http://willcode4beer.com/opinion.jsp?set=maven2_integration-test Sprawdzę, czy mogę to zrobić za pomocą pluginów lub nawet z wtyczką Maven Resources (zasoby: copy-resources) – Puce

4

I rozszerzył GenericXmlContextLoader

public class MyContextLoader extends GenericXmlContextLoader {

i overrote metodę

protected String[] generateDefaultLocations(Class<?> clazz)

zbierać nazwy pliku konfiguracyjnym z katalogu, który mogę określ przez SystemProperty (-Dtest.config =).

ja też zmodyfikował metodę follwowing aby nie modyfikować lokalizacje

@Override 
protected String[] modifyLocations(Class<?> clazz, String... locations) { 
    return locations; 
} 

Używam tego ładowarka kontekstowe jak ten

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(loader = MyContextLoader.class) 
public class Test { .... } 

Running test z SystemProperty wskazujący źródło plików config Włącza teraz możesz używać zupełnie różnych konfiguracji.

Korzystanie z SystemProperty to oczywiście tylko jedna strategia określania położenia konfiguracji. Możesz robić, co chcesz w generateDefaultLocations().


Edycja:

Rozwiązanie to umożliwia używanie pełnych różnych konfiguracji kontekstu aplikacji (na przykład dla symulowanych obiektów), a nie tylko różne właściwości. Nie potrzebujesz etapu budowy, aby wdrożyć wszystko w swojej lokalizacji "classpath". Moja konkretna implementacja również użyła nazwy użytkowników jako domyślnej, aby wyszukać katalog konfiguracji (src/test/resources/{user}), jeśli nie podano właściwości systemu (ułatwia to utrzymanie określonych środowisk testowych dla wszystkich programistów w projekcie).

Korzystanie z obiektu PropertyPlaceholder jest nadal możliwe i zalecane.


EDIT:

Wiosna Wersja 3.1.0 wesprze XML profiles/Environment Abstraction który jest podobny do mojego rozwiązania i umożliwi wybór plików konfiguracyjnych dla różnych środowisk/profili.

+0

To brzmi interesująco. Dzięki! – Puce

+0

@Puce Zapoznaj się z nowymi funkcjami w nadchodzącym wydaniu Spring 3.1.0 (zobacz moją ostatnią edycję). – FrVaBe

0

Nie miałem żadnego sukcesu w używaniu kontekstu Spring 3.x: znacznik placeholder. Użyłem starego tag moda fasoli wraz z pliku właściwości i był w stanie ustanowić połączenia między moim kodu i mojej bazy danych tak:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
    <property name="location" value="/com/my/package/database.properties"/> 
</bean> 


<bean id="myDatasource" class="oracle.ucp.jdbc.PoolDataSourceFactory" 
    factory-method="getPoolDataSource"> 
    <property name="URL" value="${JDBC_URL}"/> 
    <property name="user" value="${JDBC_USERNAME}"/> 
    <property name="password" value="${JDBC_PASSWORD}"/> 
    <property name="connectionFactoryClassName" 
     value="oracle.jdbc.pool.OracleConnectionPoolDataSource"/> 
    <property name="ConnectionPoolName" value="SCDB_POOL"/> 
    <property name="MinPoolSize" value="5"/> 
    <property name="MaxPoolSize" value="50"/> 
    <property name="connectionWaitTimeout" value="30"/> 
    <property name="maxStatements" value="100"/> 
</bean> 

Oto przykład z pliku właściwości:

JDBC_URL=jdbc:oracle:thin:@myDB:1521:mySchema 
JDBC_USERNAME=username 
JDBC_PASSWORD=password 

Potem założyłem mój test JUnit tak:

@ContextConfiguration(locations = {"/com/my/pkg/test-system-context.xml"}) 
@RunWith(SpringJUnit4ClassRunner.class) 
public class HeaderDaoTest { 

    @Autowired 
    HeaderDao headerDao; 

    @Test 
    public void validateHeaderId() { 
     int headerId = 0; 

     headerId = headerDao.getHeaderId(); 

     assertNotSame(0,headerId); 
    } 

} 

który pracował dla mnie, ale każdy robi rzeczy trochę inaczej. Mam nadzieję że to pomoże.

0

Niedawno natknąłem się na ten sam problem i patrząc na documentation for the @ContextConfiguration annotation, zauważyłem opcję dziedziczenia lokacji.

Dodając to do mojej klasy, np.

@ContextConfiguration(locations = { "/my_spring_integration_test.xml" }, inheritLocations=false) 
public class FooIntegrationTest extends FooTest 

Stwierdziłem, że udało mi się nadpisać ContextConfiguration zgodnie z potrzebami.