2016-01-05 10 views
9

Używam Play 2.4.6 z wtyczką zależności kompilacji czasu i ScalaTest. Konstruktor kontrolera ma kilka parametrów, a w ApplicationLoader go tworzę. Oto kod:Testy funkcjonalne w Play 2.4.6 podczas korzystania z czasu kompilacji DI

class BootstrapLoader extends ApplicationLoader { 
    def load(context: Context) = { 
    new AppComponents(context).application 
    } 
} 

class AppComponents(context: Context) extends BuiltInComponentsFromContext(context) with NingWSComponents { 
    lazy val router = new Routes(httpErrorHandler, authenticationController, applicationController, assets) 
    lazy val applicationController = new controllers.Application() 
    lazy val authenticationController = new controllers.Authentication()(configuration, wsApi.client) 
    lazy val assets = new controllers.Assets(httpErrorHandler) 
} 

class Authentication(implicit configuration: Configuration, val ws: WSClient) extends Controller { 
    def login = Action { implicit request => 
    Unauthorized(s"${redirectUrl}") 
    } 
} 

class AuthenticationSpec extends PlaySpec with OneAppPerSuite { 

    implicit val configuration: Configuration = app.configuration 
    implicit val wsClient: WSClient = WS.client(app) 

    "when user not logged-in" should { 
    "return Status code Unauthorized(401) with redirect url" in { 
     1 mustEqual 2 
    } 
    } 
} 

Kiedy biegnę test Dostaję następujący błąd:

[info] Exception encountered when attempting to run a suite with class name: controllers.AuthenticationSpec *** ABORTED *** 
[info] com.google.inject.ProvisionException: Unable to provision, see the following errors: 
[info] 
[info] 1) Could not find a suitable constructor in controllers.Authentication. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private. 
[info] at controllers.Authentication.class(Authentication.scala:19) 
[info] while locating controllers.Authentication 
[info]  for parameter 1 at router.Routes.<init>(Routes.scala:35) 
[info] while locating router.Routes 
[info] while locating play.api.test.FakeRouterProvider 
[info] while locating play.api.routing.Router 
[info] 
[info] 1 error 
[info] at com.google.inject.internal.InjectorImpl$2.get(InjectorImpl.java:1025) 
[info] at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051) 
[info] at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:321) 
[info] at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:316) 
[info] at play.api.Application$class.routes(Application.scala:112) 
[info] at play.api.test.FakeApplication.routes(Fakes.scala:197) 
[info] at play.api.Play$$anonfun$start$1.apply$mcV$sp(Play.scala:90) 
[info] at play.api.Play$$anonfun$start$1.apply(Play.scala:87) 
[info] at play.api.Play$$anonfun$start$1.apply(Play.scala:87) 
[info] at play.utils.Threads$.withContextClassLoader(Threads.scala:21) 

FakeApplication korzystanie GuiceApplicationBuilder, co oczywiście nie działa.

Co należy zrobić, aby uruchomić takie testy?

Dzięki

Odpowiedz

0

Otrzymujesz błąd, ponieważ testy nie są nawet w stanie uruchomić aplikację. Dzieje się tak, ponieważ używasz Dependency Injection w kontrolerach (jak sugeruje komunikat o błędzie) i musisz zadeklarować je jako classes, zamiast jako objects. Jak widać na docs:

package controllers 

import play.api.mvc._ 

class Application extends Controller { 
    def index = Action { 
     Ok("It works!") 
    } 
} 

Jeśli kontroler ma pewną zależność być wstrzykiwany, należy użyć @Inject adnotacji w konstruktorze kontrolera (ponownie, proszę zobaczyć docs). Na przykład:

package controllers 

import play.api.mvc._ 
import play.api.libs.ws._ 
import javax.inject._ 

class Application @Inject() (ws: WSClient) extends Controller { 
    // ... 
} 

Można również przeczytać Compile Time Dependency Injection docs jeśli używasz go zamiast wykonawczego DI.

+1

Jak pisałem, używam skompilować iniekcji zależność czasową. Używam klas, a nie obiektów, ale nie używaj metody inject(), ponieważ chcę wykonać wtrysk w czasie kompilacji, a nie w czasie wykonywania. Dodam do pytania szkielet kontrolera. – roterl

+0

@roterl Czy próbowałeś [skonfigurować własny program ładujący aplikacje] (https://www.playframework.com/documentation/2.4.x/ScalaCompileTimeDependencyInjection#Application-entry-point)? – marcospereira

+0

Tak, używam go.działa kod produkcyjny, ale nie znajduję sposobu na jego inicjalizację w celu przeprowadzenia testów (do użycia z FaceRequest itp.). – roterl

0

Jestem stoi ten sam problem jak ty. Nie mam satysfakcjonującego rozwiązania, po to jedynie obejście : skończyło się na oddanie

implicit def client:WSClient = NingWSClient() 

w mojej klasie WithApplicationLoader

Znalazłem również https://github.com/leanovate/play-mockws który pozwala szydzić ws wzywa Ale to nie to, co chcemy tutaj

..
1
override implicit lazy val app = new BootstrapLoader().load(
ApplicationLoader.createContext(
    new Environment(
    new File("."), ApplicationLoader.getClass.getClassLoader, Mode.Test))) 

Działa podczas odtwarzania 2.5.1

+0

Skąd pochodzi 'BootstrapLoader'? Nie mogę go znaleźć w dokumentacji Play. – gregghz

Powiązane problemy