2010-09-20 7 views
17

Ich versuche, die Integrationstests in unserer Umgebung zu beschleunigen. Alle unsere Klassen sind autowired.Federintegrationstest ist langsam mit Autowiring

<context:annotation-config/> 
<context:component-scan base-package="com.mycompany.framework"/> 
<context:component-scan base-package="com.mycompany.service"/> 
...additional directories 

Ich habe bemerkt, dass Frühling alle Verzeichnisse oben angegeben ist das Scannen und wiederholt sich dann über jede Bohne und speichert die Eigenschaften jedes einzelnen: In unserer applicationContext.xml Datei haben wir die folgenden definiert. (Ich ging über die DEBUG-Nachrichten von Quelle)

Als Ergebnis nimmt der folgende Test etwa 14 Sekunden laufen:

public class MyTest extends BaseSpringTest { 
    @Test 
    def void myTest(){ 
    println "test" 
    } 
} 

Gibt es eine Möglichkeit zu faul Last der Konfiguration? Ich habe versucht, default-lazy-init="true" hinzuzufügen, aber das hat nicht funktioniert.

Im Idealfall werden nur die für den Test erforderlichen Beans instanziiert.

danke im voraus.

Update: Ich hätte dies schon früher gesagt, ich möchte nicht eine Kontextdatei für jeden Test haben. Ich glaube auch nicht, dass eine Kontextdatei nur für die Tests funktionieren würde. (Diese Testkontextdatei würde am Ende alles enthalten)

Antwort

1

Dies ist der Preis, den Sie für die automatische Erkennung von Komponenten bezahlen - es ist langsamer. Obwohl Ihr Test nur bestimmte Beans benötigt, ist Ihr <context:component-scan> viel breiter und Spring instanziiert und initialisiert jede gefundene Bean.

Ich empfehle, dass Sie eine andere Bean-Datei für Ihre Tests verwenden, eine, die nur die für den Test selbst benötigten Beans definiert, d. H. Nicht <context:component-scan>.

+0

Damit die Idee, einen separaten Anwendungskontext ist mit (e) für Test (s) ... wenn ich eine test1 Klasse, wo ich verwenden müssen eine andere Bean, ich füge es dem test1-Kontext hinzu und benutze keine Komponenten-Scans. Oder benutze den Komponenten-Scan und versuche, Tests nach Paketen zu erstellen, was Unsinn zu sein scheint. – lisak

+0

Ich dachte darüber nach, aber ich versuchte zu vermeiden, eine benutzerdefinierte Kontextdatei pro Testdatei zu definieren. Wir verwenden diese Tests für partielle Integrationstests (sie müssen die DB treffen). – Tihom

1

Wahrscheinlich, was Sie brauchen, ist Ihre Konfiguration umzuformen, um weniger Autowiring zu verwenden. Mein Ansatz ist fast immer verdrahten die Bohnen mit Namen, versuchen, explizit mit dem Design zu sein, aber auch nicht zu ausführlich, mit Autowiring, wenn klar ist, dass Sie es verwenden, um kleinere Details zu verbergen.

Nachtrag: Wenn das nicht genug ist, und Sie sind mit JUnit, können Sie ein Dienstprogramm aus dem JUnit Addons Projekt verwenden möchten. Die Klasse DirectorySuiteBuilder baut dynamisch eine Testsuite aus einer Verzeichnisstruktur auf. So können Sie zum Beispiel

DirectorySuiteBuilder builder = new DirectorySuiteBuilder(); 
Test suite = builder.suite("project/tests"); 

initialisieren Den Spring-Kontext vor diesem Code initialisieren, können Sie alle Tests gleichzeitig ausführen. Wenn jedoch jeder Test einen "sauberen" Spring-Kontext annimmt, sind Sie wahrscheinlich verloren.

+0

Das Projekt ist groß genug, dass dies nicht wirklich machbar wäre. Wir haben fast alles autowired. – Tihom

2

Ein Ansatz besteht darin, die automatische Erkennung vollständig zu überspringen und entweder einen separaten Kontext (mit den für den Test erforderlichen Komponenten) zu laden oder die Beans zur Laufzeit neu zu definieren (vor dem Testlauf).

Dieser Thread diskutiert Neudefinition von Bohnen und eine benutzerdefinierte Testklasse, dies zu tun:

Spring beans redefinition in unit test environment

+0

+1 für das Hinweisen auf Dinge, die ich nicht über Frühlingstests wusste – Ither

0

In einer solchen Situation, müssen Sie eine Balance finden. Auf der einen Seite möchten Sie zu Recht die Tests in kürzester Zeit durchführen, um die Ergebnisse schnell zu erhalten. Dies ist besonders wichtig, wenn Sie in einer Teamumgebung mit kontinuierlicher Integration arbeiten. Auf der anderen Seite möchten Sie auch zu Recht die Konfiguration der Tests so einfach wie möglich halten, damit die Wartung der Testsuite nicht zu mühsam wird, um nützlich zu sein.

Aber am Ende des Tages müssen Sie Ihr eigenes Gleichgewicht finden und eine Entscheidung treffen. Ich würde empfehlen, ein paar Kontextkonfigurationsdateien zum Testen zu erstellen, um einige Tests zu gruppieren, so dass ein einfacher Test nicht lange dauern würde und einfach von Spring konfiguriert werden muss, während die Anzahl der Konfigurationsdateien auf ein Minimum beschränkt bleibt.

14

Wenn Sie wirklich Kontext Ihrer Anwendung beschleunigen möchten, deaktivieren Sie Ihre < Komponente-Scan und führt die folgende Routine bevor Sie einen Test

Resource resource = new ClassPathResource(<PUT_XML_PATH_RIGHT_HERE>); // source.xml, for instance 
InputStream in = resource.getInputStream(); 

Document document = new SAXReader().read(in); 
Element root = document.getRootElement(); 

/** 
    * remove component-scanning 
    */ 
for (Iterator i = root.elementIterator(); i.hasNext();) { 
    Element element = (Element) i.next(); 

    if(element.getNamespacePrefix().equals("context") && element.getName().equals("component-scan")) 
     root.remove(element); 
} 

in.close(); 

ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(true); 
for (String source: new String[] {"com.mycompany.framework", "com.mycompany.service"}) { 
    for (BeanDefinition bd: scanner.findCandidateComponents(source)) { 
     root 
     .addElement("bean") 
     .addAttribute("class", bd.getBeanClassName()); 
    } 
} 

//add attribute default-lazy-init = true 
root.addAttribute("default-lazy-init","true"); 

/** 
    * creates a new xml file which will be used for testing 
    */ 
XMLWriter output = new XMLWriter(new FileWriter(<SET_UP_DESTINATION_RIGHT_HERE>)); 
output.write(document); 
output.close(); 

Außerdem läuft, ermöglichen < Kontext: annotation- config/>

Da Sie die Routine oben ausführen müssen, bevor Sie einen Test ausführen, können Sie erstellen ein bstract Klasse, wo Sie die folgende

eine Java-Systemeigenschaft laufen für Testumgebung einrichten wie

folgt
-Doptimized-application-context=false 

Und

public abstract class Initializer { 

    @BeforeClass 
    public static void setUpOptimizedApplicationContextFile() { 
     if(System.getProperty("optimized-application-context").equals("false")) { 
      // do as shown above 

      // and 

      System.setProperty("optimized-application-context", "true"); 
     } 

    } 

} 

nun für jede Testklasse, erstreckt sich nur Initializer

+0

@mkoryak Und aktivieren ** default-lazy-init = "true" ** –

+0

Wir fanden, dass das Scannen dauerte etwa 4 Sekunden und die Erstellung der Abhängigkeit Baum nahm ungefähr 10 Sekunden. Reduziert das die Scanzeit oder die Abhängigkeitsbaumzeit oder beides? – Tihom

+0

@Tihom Sie müssen nur einmal ausführen. Nichts anderes. Es wird sein XML-Gegenstück erstellen und Reflektion Overhead vermeiden –

0

Da keine der Antworten hier dieses Problem für mich gelöst hat, füge ich meine eigenen Erfahrungen hinzu.

Mein Problem war, dass Spring, Hibernate und EhCache in dem Versuch gruppiert, meine Konsole mit ausführlichen DEBUG Nachrichten zu ertränken, resultierend unlesbares Protokoll und - weit schlechter - unerträglich niedrige Leistung.

konfigurieren ihre Protokollebenen festgelegt auf den Punkt:

Logger.getLogger("org.hibernate").setLevel(Level.INFO); 
Logger.getLogger("net.sf.ehcache").setLevel(Level.INFO); 
Logger.getLogger("org.springframework").setLevel(Level.INFO);