2016-01-11 24 views
12

का परीक्षण कैसे करें मैंने अभी Akka HTTP Request-Level Client-Side API (Future-Based) का परीक्षण करना शुरू कर दिया है। एक बात यह है कि मैं यह समझने के लिए संघर्ष कर रहा हूं कि इसके लिए यूनिट टेस्ट कैसे लिखना है। प्रतिक्रिया का नकल करने का कोई तरीका है और वास्तव में HTTP अनुरोध करने के बिना भविष्य पूरा हो गया है?क्लाइंट-साइड अक्का HTTP

मैं, एपीआई और testkit पैकेज देख रहा था मैं यह कैसे इस्तेमाल कर सकते हैं, केवल डॉक्स में पता लगाने के लिए कि यह वास्तव में कहते हैं देखने के लिए कोशिश कर रहा है:

अक्का-http-testkit एक परीक्षण दोहन ​​और पुष्टि सर्वर साइड सेवा कार्यान्वयन

मैं कुछ TestServer (अक्का स्ट्रीम के लिए TestSource की तरह थोड़े) सोच रहा था के लिए उपयोगिताओं के सेट और उम्मीद प्रतिक्रिया बनाने के लिए सर्वर साइड मार्ग डीएसएल का उपयोग करें और किसी भी तरह इस ऊपर हुकHttp ऑब्जेक्ट।

object S3Bucket { 

    def sampleTextFile(uri: Uri)(
    implicit akkaSystem: ActorSystem, 
    akkaMaterializer: ActorMaterializer 
): Future[String] = { 
    val request = Http().singleRequest(HttpRequest(uri = uri)) 
    request.map { response => Unmarshal(response.entity).to[String] } 
    } 
} 
+1

मैंने कभी इसका इस्तेमाल नहीं किया, लेकिन यह स्प्रे टेस्टकिट की तरह दिखता है: https://github.com/theiterators/akka-http-microservice/blob/master/src/test/scala/ServiceSpec.scala। स्प्रे में आपको अक्का लाने की ज़रूरत नहीं है और सीधे मार्ग के खिलाफ परीक्षण कर सकते हैं (यह एक पीएफ है)। –

+0

क्या आप 'freeGeoIpConnectionFlow' का जिक्र कर रहे हैं? मुझे लगता है कि मैं यहाँ कुछ याद कर रहा हूँ। मैं देख सकता हूं कि यह [AkkaHttpMicroservice] में परिभाषा को ओवरराइड कर रहा है (https://github.com/theiterators/akka-http-microservice/blob/9ff6bdb67f9665817935ffe7107682e04056fa76/src/main/scala/AkkaHttpMicroservice.scala) लेकिन यह कैसे कहा जाता है 'सर्विसस्पेक' में? ऐसा लगता है कि बाइंडिंग प्राप्त करने के लिए आपको 'AkkaHttpMicroservice.apply()' को कॉल करने की आवश्यकता है। – Steiny

+1

स्प्रे/अक्काहट में रीस्ट एपीआई का परीक्षण करने के 2 तरीके हैं: 1) स्टार्टअप अभिनेता सिस्टम के रूप में आप पूरे एप्लिकेशन को चलाएंगे, इसे http क्लाइंट के साथ परीक्षण करें, इसे बंद करें; 2) डीएसएल को रूट करने के खिलाफ परीक्षण जो अनिवार्य रूप से एक पीएफ है और उसे चलाने के लिए अभिनेता सिस्टम की आवश्यकता नहीं है। मैं दूसरे विकल्प के बाद हूं क्योंकि यह अधिक हल्का और यूनिट टेस्ट बनाम एकीकरण परीक्षण (1) की तरह है। इस मामले में हमें नेटवर्क इंटरफ़ेस से जुड़ना नहीं होगा और मार्ग को संभालने के लिए कोई अभिनेता शुरू करने की आवश्यकता नहीं है, जब तक कि आप कहीं और कलाकारों का उपयोग न करें। स्प्रे अनुभव से बोलते हुए मैंने कभी AkkaHttp पर यह कोशिश नहीं की। –

उत्तर

4

मैं सामान्य शब्दों आप पहले से ही तथ्य यह है कि सबसे अच्छा तरीका प्रतिक्रिया उपहास करने के लिए है पर पहुंच जाते हैं में लगता है:

यहाँ समारोह क्या करता है कि मैं परीक्षण करना चाहते हैं की एक सरल उदाहरण है। स्काला में, इस स्काला नकली http://scalamock.org/

का उपयोग कर आप अपने कोड की व्यवस्था तो किया जा सकता है ताकि akka.http.scaladsl.HttpExt के अपने उदाहरण निर्भरता कोड है जो इसे इस्तेमाल करता है में इंजेक्ट किया जाता है (जैसे एक निर्माता पैरामीटर के रूप में), तो परीक्षण के दौरान आप एक इंजेक्षन कर सकते हैं Http का उपयोग करके निर्मित एक के बजाय mock[HttpExt] का उदाहरण लागू करें।

संपादित करें: मुझे लगता है कि यह पर्याप्त विशिष्ट नहीं होने के कारण मतदान किया गया था। यहां बताया गया है कि मैं आपके परिदृश्य की नकल कैसे करूंगा। यह सभी implicitis द्वारा थोड़ा और जटिल बना दिया गया है।

main में कोड:

import akka.actor.ActorSystem 
import akka.http.scaladsl.Http 
import akka.http.scaladsl.model.{Uri, HttpResponse, HttpRequest} 
import akka.http.scaladsl.unmarshalling.Unmarshal 
import akka.stream.ActorMaterializer 

import scala.concurrent.{ExecutionContext, Future} 

trait S3BucketTrait { 

    type HttpResponder = HttpRequest => Future[HttpResponse] 

    def responder: HttpResponder 

    implicit def actorSystem: ActorSystem 

    implicit def actorMaterializer: ActorMaterializer 

    implicit def ec: ExecutionContext 

    def sampleTextFile(uri: Uri): Future[String] = { 

    val responseF = responder(HttpRequest(uri = uri)) 
    responseF.flatMap { response => Unmarshal(response.entity).to[String] } 
    } 
} 

class S3Bucket(implicit val actorSystem: ActorSystem, val actorMaterializer: ActorMaterializer) extends S3BucketTrait { 

    override val ec: ExecutionContext = actorSystem.dispatcher 

    override def responder = Http().singleRequest(_) 
} 

test में कोड:

import akka.actor.ActorSystem 
import akka.http.scaladsl.model._ 
import akka.stream.ActorMaterializer 
import akka.testkit.TestKit 
import org.scalatest.{BeforeAndAfterAll, WordSpecLike, Matchers} 
import org.scalamock.scalatest.MockFactory 
import scala.concurrent._ 
import scala.concurrent.duration._ 
import scala.concurrent.Future 

class S3BucketSpec extends TestKit(ActorSystem("S3BucketSpec")) 
with WordSpecLike with Matchers with MockFactory with BeforeAndAfterAll { 


    class MockS3Bucket(reqRespPairs: Seq[(Uri, String)]) extends S3BucketTrait{ 

    override implicit val actorSystem = system 

    override implicit val ec = actorSystem.dispatcher 

    override implicit val actorMaterializer = ActorMaterializer()(system) 

    val mock = mockFunction[HttpRequest, Future[HttpResponse]] 

    override val responder: HttpResponder = mock 

    reqRespPairs.foreach{ 
     case (uri, respString) => 
     val req = HttpRequest(HttpMethods.GET, uri) 
     val resp = HttpResponse(status = StatusCodes.OK, entity = respString) 
     mock.expects(req).returning(Future.successful(resp)) 
    } 
    } 

    "S3Bucket" should { 

    "Marshall responses to Strings" in { 
     val mock = new MockS3Bucket(Seq((Uri("http://example.com/1"), "Response 1"), (Uri("http://example.com/2"), "Response 2"))) 
     Await.result(mock.sampleTextFile("http://example.com/1"), 1 second) should be ("Response 1") 
     Await.result(mock.sampleTextFile("http://example.com/2"), 1 second) should be ("Response 2") 
    } 
    } 

    override def afterAll(): Unit = { 
    val termination = system.terminate() 
    Await.ready(termination, Duration.Inf) 
    } 
} 

build.sbt निर्भरता:

libraryDependencies += "com.typesafe.akka" % "akka-http-experimental_2.11" % "2.0.1" 

libraryDependencies += "org.scalamock" %% "scalamock-scalatest-support" % "3.2" % "test" 

libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.6" 

libraryDependencies += "com.typesafe.akka" % "akka-testkit_2.11" % "2.4.1" 
+0

मैंने एक उदाहरण जोड़ा है। – Steiny

+0

इस तरह से कुछ समस्याएं हैं जो मेरे विरोध में हैं। 'S3BucketTrait' विशेषता को अन्य प्रकारों में मिश्रित नहीं किया जा सकता है, जिसकी प्रतिक्रिया को नकल करने की आवश्यकता होती है क्योंकि इसमें 'sampleTextFile' और अनारक्षित प्रकार है। यह भी एक नकली वस्तु का परीक्षण करने के लिए एक कोड गंध की तरह लगता है भले ही हम जिस फ़ंक्शन को बुला रहे हैं उसे ओवरराइड नहीं किया जा रहा है। ऐसा लगता है कि एकमात्र विकल्प है कि 'HttpResponder' को फ़ंक्शन के पैरामीटर के रूप में या केक पैटर्न का उपयोग करें। – Steiny

+1

पर्याप्त मेला, हालांकि मैं अपने मूल उदाहरण को पूरी तरह से एक सही मामले में फिर से लिखने की कोशिश नहीं कर रहा था, केवल अपने प्रश्न का उत्तर देने के लिए स्कैलामोक का उपयोग करने के तरीके को दिखाने के लिए परिवर्तनों को जरूरी बनाने के लिए आवश्यक है "क्या प्रतिक्रिया का मज़ाक करने का कोई तरीका है और वास्तव में एक HTTP अनुरोध करने के बिना भविष्य पूरा हो गया? " यदि आप केक पैटर्न के साथ जाते हैं, तो आप अभी भी 'HttpResponder' – mattinbits

4

ध्यान में रखते हुए कि आप वास्तव में अपने HTTP ग्राहक के लिए एक इकाई परीक्षण लिखना चाहते हैं आपको नाटक करना चाहिए कि कोई आर नहीं है ईल सर्वर और नेटवर्क सीमा पार नहीं, अन्यथा आप स्पष्ट रूप से एकीकरण परीक्षण करेंगे। इस तरह के मामलों में यूनिट-टेस्टेबल अलगाव को लागू करने के लिए एक लंबी ज्ञात नुस्खा इंटरफेस और कार्यान्वयन को विभाजित करना है। बस एक बाहरी HTTP सर्वर के लिए एक इंटरफेस सार संक्षेप का उपयोग और निम्नलिखित स्केच

import akka.actor.Actor 
import akka.pattern.pipe 
import akka.http.scaladsl.HttpExt 
import akka.http.scaladsl.model.{HttpRequest, HttpResponse, StatusCodes} 
import scala.concurrent.Future 

trait HTTPServer { 
    def sendRequest: Future[HttpResponse] 
} 

class FakeServer extends HTTPServer { 
    override def sendRequest: Future[HttpResponse] = 
    Future.successful(HttpResponse(StatusCodes.OK)) 
} 

class RealServer extends HTTPServer { 

    def http: HttpExt = ??? //can be passed as a constructor parameter for example 

    override def sendRequest: Future[HttpResponse] = 
    http.singleRequest(HttpRequest(???)) 
} 

class HTTPClientActor(httpServer: HTTPServer) extends Actor { 

    override def preStart(): Unit = { 
    import context.dispatcher 
    httpServer.sendRequest pipeTo self 
    } 

    override def receive: Receive = ??? 
} 

में के रूप में अपनी असली और नकली कार्यान्वयन को परिभाषित करने और FakeServer साथ संयोजन के रूप में अपने HTTPClientActor का परीक्षण करें।

+0

' के कार्यान्वयन को प्रदान करने के लिए स्कैलामोक का उपयोग कर सकते हैं। क्या एक्का स्ट्रीम (जैसे HTTP प्रतिक्रिया निकाय की तरह) का उपयोग किए बिना किसी फ़ंक्शन को यूनिट-टेस्ट करना संभव है? ActorSystem? अनुरोध विधि का नकल करना और प्रतिक्रिया ऑब्जेक्ट को प्रतिस्थापित करना बहुत आसान लगता है, लेकिन प्रतिक्रिया निकाय को उपभोग करने के लिए एक उर्वरक की आवश्यकता होती है, जो कि अभिनेता सिस्टम –

1

मैं वहाँ उम्मीद थी परीक्षण अभिनेता प्रणाली की लेकिन वह के अभाव में किसी प्रकार का (या कुछ अन्य मुहावरेदार रास्ता) का लाभ उठाने के मैं शायद कुछ इस तरह करने जा रहा हूँ एक तरीका हो सकता है:

object S3Bucket { 

    type HttpResponder = HttpRequest => Future[HttpResponse] 

    def defaultResponder = Http().singleRequest(_) 

    def sampleTextFile(uri: Uri)(
    implicit akkaSystem: ActorSystem, 
    akkaMaterializer: ActorMaterializer, 
    responder: HttpResponder = defaultResponder 
): Future[String] = { 
    val request = responder(HttpRequest(uri = uri)) 
    request.map { response => Unmarshal(response.entity).to[String] } 
    } 
} 

फिर मेरे परीक्षण में मैं सिर्फ एक नकली HttpResponder प्रदान कर सकता हूं।

+0

पर निर्भर दिखता है, मैं शायद अन्य प्रकारों में मिश्रित किया जा सकता है जिसे अन्य प्रकारों में मिश्रित किया जा सकता है वह HTTP अनुरोध करता है। शायद फ़्लो संस्करण के लिए भी कुछ जोड़ना भी। – Steiny

संबंधित मुद्दे