Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define function returning an App value? #1147

Closed
sbidin opened this issue Oct 22, 2019 · 4 comments
Closed

Define function returning an App value? #1147

sbidin opened this issue Oct 22, 2019 · 4 comments

Comments

@sbidin
Copy link

sbidin commented Oct 22, 2019

Is it possible to define a function returning an App value?

Something like this:

pub fn get_app() -> CERTAIN_TYPE_GOES_HERE {
    App::new()
        .configure(configure_app)
        .wrap(middleware::Logger::default())
}

I find no examples online of anything like this.

Without this, I don't think it's possible to have nice tests.

Each test would need to construct the App value by hand instead of being able to call a function to get it or better yet use a fixture so nice teardown is available as well. My App has multiple middlewares and some of them (like ŀogger) use types that seem to be private (like StreamLog). I know about .configure and I use it, but it doesn't help with having to set up middlewares in every test.

@sbidin sbidin changed the title How to return an App value? Define function returning an App value? Oct 22, 2019
@fafhrd91
Copy link
Member

use App::configure(), you can try to express type via impl Trait feature, but it is not supported by default.

@sbidin
Copy link
Author

sbidin commented Oct 23, 2019

I'm already using App::configure, I'm concerned about duplicating the entire App creation process with middleware included, not only its routes etc. Trying it out with impl Trait led me to a dead end.

Anyway, I've noticed other Github issues with the same concern about App and tests, but no answers as far as I can tell. One can avoid this duplicated App issue in tests by packing the App into a Service, like so:

use actix_http::{Error, Request};
use actix_service::{Service};
use actix_web::dev::{ServiceResponse};
use actix_web::{middleware, test, App};
use actix_web::body::MessageBody;

pub fn get_app() -> impl Service<
    Request=Request,
    Response=ServiceResponse<impl MessageBody>,
    Error=Error,
> {
    test::init_service(
        App::new()
            .configure(/* Add your config handler here. */)
            .wrap(middleware::Logger::default()),
    )
}

Then it's reusable, say, like so:

use actix_service::Service;
use actix_web::http::StatusCode;
use actix_web::test::{block_on, TestRequest};

#[test]
fn test_some_route() {
    let mut app = get_app();
    let req = TestRequest::get().uri("/some/route").to_request();
    let rsp = block_on(app.call(req)).unwrap();
    assert_eq!(rsp.status(), StatusCode::OK);
}

This compiles, allows me to have the App definition in only one place in tests, and seems to work nicely.

@fafhrd91
Copy link
Member

It is not possible in any case. impl Trait is an only option

@OmegaJak
Copy link

OmegaJak commented Apr 15, 2023

I'm already using App::configure, I'm concerned about duplicating the entire App creation process with middleware included, not only its routes etc. Trying it out with impl Trait led me to a dead end.

Anyway, I've noticed other Github issues with the same concern about App and tests, but no answers as far as I can tell. One can avoid this duplicated App issue in tests by packing the App into a Service, like so:

use actix_http::{Error, Request};
use actix_service::{Service};
use actix_web::dev::{ServiceResponse};
use actix_web::{middleware, test, App};
use actix_web::body::MessageBody;

pub fn get_app() -> impl Service<
    Request=Request,
    Response=ServiceResponse<impl MessageBody>,
    Error=Error,
> {
    test::init_service(
        App::new()
            .configure(/* Add your config handler here. */)
            .wrap(middleware::Logger::default()),
    )
}

Then it's reusable, say, like so:

use actix_service::Service;
use actix_web::http::StatusCode;
use actix_web::test::{block_on, TestRequest};

#[test]
fn test_some_route() {
    let mut app = get_app();
    let req = TestRequest::get().uri("/some/route").to_request();
    let rsp = block_on(app.call(req)).unwrap();
    assert_eq!(rsp.status(), StatusCode::OK);
}

This compiles, allows me to have the App definition in only one place in tests, and seems to work nicely.

The function signature to accomplish this in main code can look something like this:

fn create_app() -> App<
	impl ServiceFactory<
		ServiceRequest,
		Config = (),
		Response = ServiceResponse<impl MessageBody>,
		Error = Error,
		InitError = (),
	>,
> {
	App::new()
}

which can then be used by inside the get_app method shown above for tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants