2012-04-06 10 views
12

Alle,Wie zu fälschen Initial mit Standardkonstruktors

Ich versuche, einige Unit-Tests in einigen archaischen Java-Code (keine Schnittstellen, keine Abstraktion, etc.) zu tun

Dies ist ein Servlet, das Verwendungen Ein ServletContext (von dem ich annahm, dass er von Tomcat eingerichtet wurde) und in der Datei web.xml/context.xml sind Datenbankinformationen eingerichtet. Jetzt habe ich herausgefunden, wie eine gefälschte ServletContext zu machen, aber der Code hat

InitialContext _ic = new InitialContext(); 

ganzen Ort (es ist also nicht möglich, sie zu ersetzen). Ich muss einen Weg finden, um eine Standard-InitialContext() in der Lage zu machen, _ic.lookup(val) ohne eine Ausnahme zu werfen.

Ich nehme an, es gibt eine Möglichkeit, dass die context.xml geladen wird, aber wie diese Magie funktioniert, zeichne ich eine leere. Hat jemand Ideen?

+0

Nur weil es oft vorkommt, heißt das nicht, dass es definitiv nicht durchführbar ist, es zu ersetzen. Verdammt, selbst wenn man nur eine statische Factory-Methode verwendet, würde dies * mehr * Testbarkeit ermöglichen (obwohl es eindeutig nicht so schön ist wie einige Alternativen). –

Antwort

3

Sie können PowerMock verwenden, um die Konstruktion des InitialContext zu verspotten und sein Verhalten zu steuern. Konstruktor Mocking ist dokumentiert here.

PowerMock-Tests können ziemlich unordentlich und kompliziert sein, Refactoring ist normalerweise eine bessere Option.

+0

+1. PowerMock ist mächtig, aber es hat definitiv genug Kopfschmerzen verursacht, dass Refactoring sehr bevorzugt wird. – AHungerArtist

+0

Ich denke, das ist die beste Lösung für das, was ich versuche zu tun. Vielen Dank! – Austin

+0

Vermeiden Sie PowerMock, es sei denn, Sie haben keine andere Option offen ... Viele Artikel erklären warum. –

4

Versuchen Sie, die Systemvariablen der Einrichtung vor:

System.setProperty(Context.INITIAL_CONTEXT_FACTORY, 
     "org.apache.naming.java.javaURLContextFactory"); 
System.setProperty(Context.URL_PKG_PREFIXES, 
     "org.apache.naming"); 
InitialContext ic = new InitialContext(); 

Wenn Sie JUnit verwenden, folgen Sie diesem doc: https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit

+1

Dies gibt 'java.lang.ClassNotFoundException: org.apache.naming.java.javaURLContextFactory' –

+1

Sie sollten die Tomcat-Umgebung in Ihrem Projekt haben. Sie müssen die Tomcat-Gläser hinzufügen –

+0

Also wenn ich JBoss verwende ich denke, ich muss JBoss-Umgebung verwenden. Vielen Dank! –

6

Hier ist meine Lösung die Inintial Kontext für meine Unit-Tests einrichten. Zuerst habe ich die folgenden Test Abhängigkeit mein Projekt:

<dependency> 
    <groupId>org.apache.tomcat</groupId> 
    <artifactId>catalina</artifactId> 
    <version>6.0.33</version> 
    <scope>test</scope> 
</dependency> 

Dann habe ich eine statische Methode mit dem folgenden Code erstellt:

public static void setupInitialContext() throws Exception { 
    System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory"); 
    System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming"); 
    InitialContext ic = new InitialContext(); 
    ic.createSubcontext("jdbc"); 
    PGSimpleDataSource ds = new PGSimpleDataSource(); 
    ds.setDatabaseName("postgres"); 
    ds.setUser("postgres"); 
    ds.setPassword("admin"); 
    ic.bind("jdbc/something", ds); 
} 

schließlich in jedem meiner Testklasse füge ich ein @BeforeClass Methode, die ruft setupInitialContext auf.

+0

in Bezug auf diese https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit war sehr gut – hephestos

35

Vorteil der Tatsache, dass InitialContext eine SPI verwendet, um seine Schöpfung zu handhaben. Sie können sich in den Lebenszyklus einklinken, indem Sie eine Implementierung von javax.naming.spi.InitialContextFactory erstellen und diese über die Systemeigenschaft javax.naming.factory.initial (Context.INTITIAL_CONTEXT_FACTORY) an Ihre Tests übergeben. Es ist einfacher als es klingt.

diese Klasse Gegeben:

public class UseInitialContext { 

    public UseInitialContext() { 
     try { 
      InitialContext ic = new InitialContext(); 
      Object myObject = ic.lookup("myObject"); 
      System.out.println(myObject); 
     } catch (NamingException e) { 
      e.printStackTrace(); 
     } 
    } 


} 

Und das impl von InitialContextFactory:

public class MyInitialContextFactory implements InitialContextFactory { 

    public Context getInitialContext(Hashtable<?, ?> arg0) 
      throws NamingException { 

     Context context = Mockito.mock(Context.class); 
     Mockito.when(context.lookup("myObject")).thenReturn("This is my object!!"); 
     return context; 
    } 
} 

eine Instanz von UseInitialContext in einem JUnit-Test mit

-Djava.naming.initial.factory=initial.context.test.MyInitialContext 

auf den Zeilenbefehl Erstellen Ausgänge This is my object!! (einfach in der Sonnenfinsternis einzurichten). Ich mag Mockito für Spott und Stubbing. Ich würde auch Michael Feather's Working Effectively with Legacy Code empfehlen, wenn Sie mit viel Legacy-Code umgehen. Es geht darum, Nähte in Programmen zu finden, um bestimmte Teile zum Testen zu isolieren.

+0

Anstelle von "java.naming.initial.factory", denke ich, das richtige System Eigenschaft ist 'java.naming.factory.initial'. Zumindest in Java 6. Danke für die Post! – marciopd

+3

'java.naming.factory.initial' ist auch für Java 7. –

+0

FWIW, ich schrieb eine sehr ähnliche Antwort einige Zeit später, und ich tat dies [mit einem JUnit 'TestTule', um das Setup zu handhaben und Teardown] (http://stackoverflow.com/a/17083737/116639). –

1

Heute habe ich das gleiche Problem konfrontiert (wir nicht vom Benutzer PowerMock kann) und es auf diese Weise gelöst:

  1. nicht so im Konstruktor Nachschlag Sie, wenn Sie @InitMock auf das Objekt aufrufen, Der Konstruktor benötigt den Kontext noch nicht.

  2. Erstellen Sie eine Methode zum Abrufen der Service-Bean bei Bedarf wie "getService() serviceMethod (param, param ...).":

/* Class ApplicationResourceProvider */ 

    /* We can mock this and set it up with InjectMocks */ 
    InitialContext ic; 

    /* method hiding the lookup */ 
    protected ApplicationService getService() throws NamingException { 
     if(ic == null) 
      ic = new InitialContext(); 
     return (ApplicationService)ic.lookup("java:global/defaultApplicationLocal"); 
    } 
  1. Auf dem Test, einstellen:
@Mock 
ApplicationService applicationServiceBean; 

@Mock 
InitialContext ic; 

@InjectMocks 
ApplicationResourceProvider arp; 

@Before 
public void setUp() throws Exception { 
    MockitoAnnotations.initMocks(this); 
    when(ic.lookup(anyString())).thenReturn(applicationServiceBean); 
    ... 
} 
Verwandte Themen