2016-06-16 13 views
1

Ich habe einen Rest-Dienst mit vier Methoden erstellt, GET, POST, UPDATE und DELETE. Diese Methoden stellen Verbindungen zu einer Datenbank her, um Daten abzurufen und zu speichern.JAXRS + JerseyTest einen REST-Dienst testen

Jetzt möchte ich jede Methode testen. Ich habe das Jersey Test Framework dafür verwendet. Und es funktioniert so lange, wie ich den Code entferne, der tatsächlich den Anruf zur Datenbank macht. Wenn ich den Code belasse, der den Aufruf an die Datenbank sendet, löst er eine Ausnahme aus, die keine Verbindung zur Datenbank herstellen konnte.

EDIT: Ich habe ein wenig Forschung und verwendet Dependance-Injektion. Die DB-Aufrufe werden in eine separate Klasse verschoben, aber ich mache immer noch etwas falsch.

DatabaseResults. In dieser Klasse wird der Aufruf an die DB vorgenommen.

public class DatabaseResults { 

private final String getQuery = "SELECT * FROM movies"; 
private Connection connection = null; 
private PreparedStatement pstmt = null; 
private final ArrayList<Movie> jsonList = new ArrayList<>(); 

public JSONObject getAllMovies() throws SQLException { 

    try { 
     ComboPooledDataSource dataSource = DatabaseUtility.getDataSource(); 
     connection = dataSource.getConnection(); 

     pstmt = connection.prepareStatement(getQuery); 
     ResultSet rs = pstmt.executeQuery(); 

     while (rs.next()) { 
      jsonList.add(new Movie(rs.getString(1), rs.getString(2), rs.getString(4), rs.getString(3))); 
     } 

    } catch (SQLException ex) { 
     System.out.println(ex); 
     System.out.println("Could not retrieve a connection"); 
     connection.rollback(); 
    } finally { 
     connection.close(); 
    } 

    JSONObject jsonObject = new JSONObject(); 
    jsonObject.put("movies", jsonList); 

    return jsonObject; 
    } 
} 

MoviesResource, die die REST Methoden

@Path("movies") 
public class MoviesResource { 

.... 
private DatabaseResults dbResults = null; 

public MoviesResource() { 
    this(new DatabaseResults()); 
} 

MoviesResource(DatabaseResults dbr){ 
    this.dbResults = dbr; 

} 
.... 
@GET 
@Produces(MediaType.APPLICATION_JSON) 
public Response getAllMovies() throws JSONException, SQLException { 

    return Response.status(200).entity(dbResults.getAllMovies().toString()).build(); 
} 

Die Test-Klasse

@RunWith(MockitoJUnit44Runner.class) 
public class MovieResourceTest extends JerseyTest { 

JSONObject jsonObject = new JSONObject(); 

@Mock 
DatabaseResults dbr; 

@Before 
public void setup() throws SQLException{ 
    jsonObject.put("id", "hello"); 

    when(dbr.getAllMovies()).thenReturn(jsonObject); 
}   

Client client = ClientBuilder.newClient(); 
WebTarget target = client 
     .target("http://localhost:9998/RestServiceMovies/resources"); 

@Override 
protected Application configure() { 
    return new ResourceConfig(MoviesResource.class); 
} 

@Test 
public void getAllMoviesTest() throws SQLException { 

    String responseGetAllMovies = target("/movies").request().get(String.class); 

    Assert.assertTrue("hello".equals(responseGetAllMovies)); 
} 

In diesem Moment enthält ich die Tests ausführen kann, aber immer noch, wenn ich die getAllMovies() Methode testen macht es einen Anruf in die echte Datenbank statt die jsonObject zurückgeben.

Ich habe das Gefühl, dass eine Verbindung zwischen dem Mock-Objekt und dem Konstruktor aus der MovieResource-Klasse fehlt?

+0

Dies ist schwieriger zu testen, da Ihr REST-Dienst das Prinzip der einheitlichen Verantwortlichkeit verletzt. Wenn Sie Ihre Datenbanklogik in einer anderen Klasse kapseln, können Sie diese Klasse einfach zum Schein testen, um den REST-Dienst isoliert zu testen. Ein anderer allgemeiner Ansatz besteht darin, das Fenster zu testen und nur Integrationstests durchzuführen. d. h. eine In-Memory-Datenbank konfigurieren und nichts vortäuschen. – DavidS

+0

Ok. Also habe ich meine Datenbanklogik in einer separaten Klasse gekapselt. Ich möchte die Daten mit Mockito verspotten, aber ich bin mir eigentlich nicht sicher, wie ich sagen kann, dass meine 'getAllMovies'-Methode die gespottete Klasse verwenden soll ... – RSSD

+0

Dort gibt es ein paar Ansätze dazu. Ich nehme an, dass Ihr REST-Dienst Zugriff auf ein privates Feld hat, z. 'FilmeDatenbank'. Sie möchten dieses Objekt oder seine Methoden vortäuschen, also benötigen Sie eine Möglichkeit, dem REST-Dienst mitzuteilen, welche 'moviesDatabase' verwendet werden soll. Ihre Optionen sind: (1) Konstruktorinjektion (2) mache das Feld package-private und setze es direkt (3) verwende ein fantastisches Abhängigkeitsinjektions-Framework wie Dolch oder Weld. – DavidS

Antwort

2

Wenn Sie Ihre Ressource als eine Klasse registrieren

new ResourceConfig(MoviesResource.class) 

Sie Jersey erzählen die Instanz zu erstellen. Wenn Sie keine DI konfiguriert haben, wird nur der Konstruktor no-arg aufgerufen. In Ihrem no-arg-Konstruktor erstellen Sie nur den Dienst selbst. Es weiß nichts über deinen Spott.

Stattdessen sollten Sie die Ressourcenklasse als Instanz registrieren. Auf diese Weise können Sie den Mock dem Konstruktor übergeben.

MockitoAnnotations.initMocks(this); 
return new ResourceConfig() 
     .register(new MoviesResource(dbr)); 

Verwenden Sie nicht den Mockito-Läufer. Verwenden Sie stattdessen die Methode MockitoAnnotations.initMocks. Auf diese Weise steuern Sie, wenn die @Mock s injiziert werden. Wenn Sie den Läufer verwenden, wird die Injektion nicht rechtzeitig erfolgen, da die Methode configure vom Framework aufgerufen wird, bevor die Mockito-Injektion erfolgt.

+0

Danke! Ich habe den Mockito Runner entfernt und die 'MockitoAnnotations.initMocks (this); return new ResourceConfig(). register (neuer MoviesResource (dbr)) 'code. – RSSD

+1

Ihr Recht, es sollte 'initMocks' sein, nicht' init' :-) –