actix / actix-web

Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.
https://actix.rs
Apache License 2.0
21.6k stars 1.67k forks source link

Problem migrating tests to 1.0 #960

Closed antoine-de closed 5 years ago

antoine-de commented 5 years ago

Problem migrating tests to 1.0

Hi,

I'm migrating a project from 0.7 to 1.0 and I'm quite happy with the migration, great work!

The only things I'm stumbling upon are migrating the integration tests.

I don't know if that's really idiomatic, but for our integration tests we used the same App creation as for the real server.

To do this, the app creation was in a function returing App<Context>.

pub fn create_server(ctx: Context) -> App<Context> {
    App::with_state(ctx)...
}

then in main:

server::new(move || create_server(ctx.clone()))
    .bind(&args.bind)?
    .workers(args.nb_threads)
    .run()

and in tests:

actix_web::test::TestServer::with_factory(move || create_server(ctx))

Now that the App type depends on the middleware I don't know the new return type for the create_server function.

Following some advice from gitter, I used App::configure:

pub fn configure_server(cfg: &mut web::ServiceConfig) {
    cfg.service(
        web::resource("/")
            .route(web::get().to(entry_point)),
    )...
}

then in main:

HttpServer::new(move || {
        App::new()
            .data(ctx.clone())
            .wrap(some_middleware)
            .wrap(other_middleware)
            .configure(configure_server)
            .default_service(web::resource("").route(web::get().to(default_404)))
    })
    .bind(&args.bind)?
    .workers(args.nb_threads)
    .run()

And in test:

let app = actix_web::App::new()
    .data(ctx.clone())
    .wrap(some_middleware)
    .wrap(other_middleware)
    .configure(configure_server)
    .default_service(web::resource("").route(web::get().to(default_404)));

With this I can share all the initialization of the routes, but I'm forced to duplicate the middleware initialization and the default_service. But well the wrap middlewares system is nice, so I guess that's a tradeoff with the typesystem.

But I'm blocked at the next step.

Before I had a helper around actix_web::test::TestServer to ease the use of the TestServer (bringing some asserts and parsing of the results).

actix v0.7:

pub struct BragiHandler {
    app: actix_web::test::TestServer,
}

impl BragiHandler {
    pub fn new(url: String) -> BragiHandler {
        let make_server = move || {
            bragi::server::create_server(bragi::Context::from(&bragi::Args {
                connection_string: url.clone(),
                ..Default::default()
            }))
        };

        BragiHandler {
            app: actix_web::test::TestServer::with_factory(make_server),
        }
    }

    pub fn raw_get(&mut self, q: &str) -> Result<ClientResponse, Error> {
        self.app
            .execute(
                self.app
                    .client(actix_web::http::Method::GET, q)
                    .finish()
                    .map_err(|e| format_err!("invalid query: {}", e))?
                    .send(),
            )
            .map_err(|e| format_err!("impossible to query bragi: {}", e))
    }
}

What seems to be the actix v1 equivalent is actix_web::test::init_service(app), but I don't see a way to store the resulting service as the resulting type is a bit complex

pub fn init_service<R, S, B, E>(
    app: R
) -> impl Service<Request = Request, Response = ServiceResponse<B>, Error = E> where
    R: IntoNewService<S>,
    S: NewService<Config = ServerConfig, Request = Request, Response = ServiceResponse<B>, Error = E>,
    S::InitError: Debug

I'm far from being a rust expert and I'm stuck to know the concrete type of the service

pub struct BragiHandler {
    app: <what type is this?>,
}

I tried boxing it but I fail the know all the sub types of NewService (especially the Response)

What is the right course of action for this ? Is it not idiomatic to want to make a wrapper around the test service ?

fafhrd91 commented 5 years ago

With init_service you can avoid any io. TestServer exists it get moved to actix-http-test

fafhrd91 commented 5 years ago

init_service is same as App::wrap, it is designed to be used in-place.

reopen if this ticket is still relevant