测试

每个应用程序都应该经过充分测 Actix提供了执行单元和集成测试的工具.

单元测试

对于单元测试,actix提供了一个请求构建器类型和一个简单的处理程序运行器。 TestRequest 实现类似构建器的模式。您可以HttpRequest使用finish()或生成实例,也可以使用run()或运行处理程序run_async()

  1. use actix_web::{http, test, HttpRequest, HttpResponse, HttpMessage};
  2. fn index(req: &HttpRequest) -> HttpResponse {
  3. if let Some(hdr) = req.headers().get(http::header::CONTENT_TYPE) {
  4. if let Ok(s) = hdr.to_str() {
  5. return HttpResponse::Ok().into()
  6. }
  7. }
  8. HttpResponse::BadRequest().into()
  9. }
  10. fn main() {
  11. let resp = test::TestRequest::with_header("content-type", "text/plain")
  12. .run(index)
  13. .unwrap();
  14. assert_eq!(resp.status(), http::StatusCode::OK);
  15. let resp = test::TestRequest::default()
  16. .run(index)
  17. .unwrap();
  18. assert_eq!(resp.status(), http::StatusCode::BAD_REQUEST);
  19. }

集成测试

有几种方法可用于测试您的应用程序。Actix提供 TestServer,可用于在真实的http服务器中使用特定处理程序运行应用程序。

TestServer::get()TestServer::post()TestServer::client() 方法可用于向测试服务器发送请求。

TestServer可以将简单表单配置为使用处理程序。 TestServer::new方法接受配置函数,此函数的唯一参数是测试应用程序实例。

有关更多信息,请查看api文档

  1. use actix_web::{HttpRequest, HttpMessage};
  2. use actix_web::test::TestServer;
  3. use std::str;
  4. fn index(req: HttpRequest) -> &'static str {
  5. "Hello world!"
  6. }
  7. fn main() {
  8. // start new test server
  9. let mut srv = TestServer::new(|app| app.handler(index));
  10. let request = srv.get().finish().unwrap();
  11. let response = srv.execute(request.send()).unwrap();
  12. assert!(response.status().is_success());
  13. let bytes = srv.execute(response.body()).unwrap();
  14. let body = str::from_utf8(&bytes).unwrap();
  15. assert_eq!(body, "Hello world!");
  16. }

另一种选择是使用应用程序工厂。在这种情况下,您需要以与实际http服务器配置相同的方式传递工厂函数。

  1. use actix_web::{http, test, App, HttpRequest, HttpResponse};
  2. fn index(req: &HttpRequest) -> HttpResponse {
  3. HttpResponse::Ok().into()
  4. }
  5. /// This function get called by http server.
  6. fn create_app() -> App {
  7. App::new()
  8. .resource("/test", |r| r.h(index))
  9. }
  10. fn main() {
  11. let mut srv = test::TestServer::with_factory(create_app);
  12. let request = srv.client(
  13. http::Method::GET, "/test").finish().unwrap();
  14. let response = srv.execute(request.send()).unwrap();
  15. assert!(response.status().is_success());
  16. }

如果需要更复杂的应用程序配置,请使用该TestServer::build_with_state() 方法。例如,您可能需要初始化应用程序状态或启动SyncActor diesel进程。此方法接受构造应用程序状态的闭包,并在配置actix系统时运行。因此,您可以初始化任何其他actor。

  1. #[test]
  2. fn test() {
  3. let srv = TestServer::build_with_state(|| {
  4. // we can start diesel actors
  5. let addr = SyncArbiter::start(3, || {
  6. DbExecutor(SqliteConnection::establish("test.db").unwrap())
  7. });
  8. // then we can construct custom state, or it could be `()`
  9. MyState{addr: addr}
  10. })
  11. // register server handlers and start test server
  12. .start(|app| {
  13. app.resource(
  14. "/{username}/index.html", |r| r.with(
  15. |p: Path<PParam>| format!("Welcome {}!", p.username)));
  16. });
  17. // now we can run our test code
  18. );

流响应测试

如果您需要测试流,将ClientResponse转换为future并执行它就足够了。例如,测试Server Sent Events

  1. extern crate bytes;
  2. extern crate futures;
  3. extern crate actix_web;
  4. use bytes::Bytes;
  5. use futures::stream::poll_fn;
  6. use futures::{Async, Poll, Stream};
  7. use actix_web::{HttpRequest, HttpResponse, Error};
  8. use actix_web::http::{ContentEncoding, StatusCode};
  9. use actix_web::test::TestServer;
  10. fn sse(_req: HttpRequest) -> HttpResponse {
  11. let mut counter = 5usize;
  12. // yields `data: N` where N in [5; 1]
  13. let server_events = poll_fn(move || -> Poll<Option<Bytes>, Error> {
  14. if counter == 0 {
  15. return Ok(Async::NotReady);
  16. }
  17. let payload = format!("data: {}\n\n", counter);
  18. counter -= 1;
  19. Ok(Async::Ready(Some(Bytes::from(payload))))
  20. });
  21. HttpResponse::build(StatusCode::OK)
  22. .content_encoding(ContentEncoding::Identity)
  23. .content_type("text/event-stream")
  24. .streaming(server_events)
  25. }
  26. fn main() {
  27. // start new test server
  28. let mut srv = TestServer::new(|app| app.handler(sse));
  29. // request stream
  30. let request = srv.get().finish().unwrap();
  31. let response = srv.execute(request.send()).unwrap();
  32. assert!(response.status().is_success());
  33. // convert ClientResponse to future, start read body and wait first chunk
  34. let (bytes, response) = srv.execute(response.into_future()).unwrap();
  35. assert_eq!(bytes.unwrap(), Bytes::from("data: 5\n\n"));
  36. // next chunk
  37. let (bytes, _) = srv.execute(response.into_future()).unwrap();
  38. assert_eq!(bytes.unwrap(), Bytes::from("data: 4\n\n"));
  39. }

WebSocket服务器测试

它可以注册一个处理程序与TestApp::handler(),从而启动一个网络套接字连接。TestServer提供了ws()连接到websocket服务器并返回ws readerwriter对象的方法。TestServer还提供了一种execute()方法,该方法将未来的对象运行完成并返回未来计算的结果。

以下示例演示如何测试websocket处理程序:

  1. use actix_web::*;
  2. use futures::Stream;
  3. struct Ws; // <- WebSocket actor
  4. impl Actor for Ws {
  5. type Context = ws::WebsocketContext<Self>;
  6. }
  7. impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
  8. fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
  9. match msg {
  10. ws::Message::Text(text) => ctx.text(text),
  11. _ => (),
  12. }
  13. }
  14. }
  15. fn main() {
  16. let mut srv = test::TestServer::new(
  17. |app| app.handler(|req| ws::start(req, Ws)));
  18. let (reader, mut writer) = srv.ws().unwrap();
  19. writer.text("text");
  20. let (item, reader) = srv.execute(reader.into_future()).unwrap();
  21. assert_eq!(item, Some(ws::Message::Text("text".to_owned())));
  22. }