2013-07-09 14 views
7

Powiedz, że mam kontroler z akcją, która otrzymuje dwa parametry.Jak mogę przetestować kontroler w strukturze gry 2 scala

To wywołuje dwie usługi, po jednej z każdego parametru, usługi oba ciągi powrotne

każdego z tych ciągów są przekazywane jako argumenty do szablonu

wynik jest przekazywany do Ok i wrócił.

Chcę napisać prosty test jednostki, która zapewnia: 1 - poprawne usługi są wywoływane z prawidłowymi parametrami 2 - Wartości powrocie z usług są przekazywane do odpowiednich atrybutów szablonu

Co to najlepszy sposób na zrobienie tego?

+0

Dlaczego nie używać ScalaCheck/ScalaTest/Specs2 .0? – 4lex1v

+0

W szczególności, w jaki sposób utworzyć test za pomocą specyfikacji2.0, aby przechwycić połączenie do usługi i wywołanie szablonu? Przykłady, które udało mi się zobaczyć testują aplikację jako całość (zintegrowaną) –

Odpowiedz

2

Korzystając z Mockito za pomocą Specs2, wykonuję fałszywe usługi, aby zweryfikować ich wywołania metod.

Mój kontroler tworzy instancję wiosną. To pozwala mi traktować to jako class zamiast object. => Jest to niezbędne, aby można było testować controller. Oto przykład:

@Controller 
class MyController @Autowired()(val myServices: MyServices) extends Controller 

Aby włączyć sprężynowe dla kontrolerów, trzeba zdefiniować Global obiektu, jak grać! dokumentacja wyjaśnia:

object Global extends GlobalSettings { 

    val context = new ClassPathXmlApplicationContext("application-context.xml") 

    override def getControllerInstance[A](controllerClass: Class[A]): A = { 
    context.getBean(controllerClass) 
    } 
} 

Mój test jednostkowy nie potrzebuje wiosny; Po prostu przekazuję współpracownikom (mocks) do konstruktora.

Jednak dotyczące świadczonych szablonu przetestować tylko dla typu wyniku (OK, BadRequest, przekierowania itp ...). Rzeczywiście, zauważyłem, że wcale nie jest łatwo sprawić, aby mój test przeskanował cały wyrenderowany szablon w szczegółach (parametry wysłane do niego itp.), Tylko z testowaniem jednostki.

Tak więc, aby potwierdzić, że właściwy szablon jest wywoływany z właściwymi argumentami, ufam moim testom akceptacyjnym z uruchomieniem Selenium lub ewentualnemu testowi funkcjonalnemu, jeśli wolisz, aby skanować cały oczekiwany wynik.

2 - Wartości powrocie z usług są przekazywane do odpowiednich atrybutów szablonu

Jest to dość łatwe do sprawdzenia that..How? Opierając się na kompilatorze! Wolisz przekazać niektóre niestandardowe typy do swojego szablonu zamiast prostych operacji, na przykład: phone: String stanie się: phone: Phone. (prosty obiekt wartości). Dlatego nie obawiaj się przekazywać atrybutów w niezapowiedzianej kolejności do szablonu (w teście jednostkowym lub rzeczywistym kodzie produkcyjnym). Kompilator rzeczywiście ostrzeże.

Oto przykład jednego z moich testów jednostkowych (uproszczony) przy użyciu specs2: (Zauważysz użycie owijki: WithFreshMocks). Ten case class pozwoli odświeżyć wszystkie zmienne (w tym przypadku próbę) test po teście. Tak więc dobrym sposobem na zresetowanie mocks.

class MyControllerSpec extends Specification with Mockito { 

     def is = 
     "listAllCars should retrieve all cars" ! WithFreshMocks().listAllCarsShouldRetrieveAllCars 

     case class WithFreshMocks() { 

     val myServicesMock = mock[MyServices] 
     val myController = new MyController(myServicesMock) 

     def listAllCarsShouldRetrieveAllCars = { 
      val FakeGetRequest = FakeRequest() //fakeRequest needed by controller 
      mockListAllCarsAsReturningSomeCars() 
      val result = myController.listAllCars(FakeGetRequest).asInstanceOf[PlainResult] //passing fakeRequest to simulate a true request 
      assertOkResult(result). 
      and(there was one(myServicesMock).listAllCars()) //verify that there is one and only one call of listAllCars. If listAllCars would take any parameters that you expected to be called, you could have precise them. 
     } 

     private def mockListAllCarsAsReturningSomeCars() { 
      myServicesMock.listAllCars() returns List[Cars](Car("ferrari"), Car("porsche")) 
     } 

     private def assertOkResult(result: PlainResult) = result.header.status must_== 200 

     } 
+1

Wymyśliłem rozwiązanie, które pozwala mi kpić z moich szablonów, ale zgadzam się na użycie systemu typu, aby uzyskać błędy kompilatora, a nie błędy testowe, gdy używanie niepoprawnych argumentów –

2

Więc wymyśliłem ciasto wzór i Mockito rozwiązanie oparte o:

danej usługi:

trait Service { 
    def indexMessage : String 
} 

trait ServiceImpl { 
    def indexMessage = { 
    "Hello world" 
    } 
} 

Następnie kontroler wygląda następująco:

object Application extends ApplicationController 
        with ServiceImpl { 
    def template = views.html.index.apply 
} 

trait ApplicationController extends Controller 
          with Service { 
    def template: (String) => play.api.templates.Html 

    def index = Action { 
    Ok(template("controller got:" + indexMessage)) 
    } 
} 

I test wygląda następująco:

class ApplicationControllerSpec extends Specification with Mockito { 
    "cake ApplicationController" should { 
     "return OK with the results of the service invocation" in { 
     val expectedMessage = "Test Message" 
     val m = mock[(String) => play.api.templates.Html] 

     object ApplicationControllerSpec extends ApplicationController { 
      def indexMessage = expectedMessage 
      def template = m 
     } 

     val response = ApplicationControllerSpec.index(FakeRequest()) 

     status(response) must equalTo(OK) 
     there was one(m).apply(Matchers.eq("controller got:" + expectedMessage)) 
     } 
    } 
} 

Miałem dużo kłopotów z pracą Mockito.
Wymaga to dodatkowej zależności i miałem sporo problemów z pracą z matowcami w scala (całkiem nieźle korzystam z tego w java).

Ostatecznie myślę, że powyższa odpowiedź jest lepsza, unikaj używania Stringa i inne prymitywne typy, w których można zawijać je w typy specyficzne dla zadania, a następnie otrzymujesz ostrzeżenia kompilatora.

Również generalnie unikam robienia takich rzeczy, jak przedrostek "controller got:" w kontrolerze.

To nie w tym przypadku, aby można było zweryfikować, że przeszedł w realnym świecie, które powinny być wykonywane przez innego składnika (sterowniki są tylko dla kanalizacji IMO)

+0

Nie działa z grą 2.3.8 – zengr

Powiązane problemy