Skip to content

Feature Request: Abstractable Types for unit testing #36

Closed
@Gisleburt

Description

@Gisleburt

One of the big reasons to choose Rust for game dev is its incredibly good built in testing tools.

Currently, the way that we wire custom classes, its not possible to write unit tests as the struct depends on the godot engine being present.

Take this overly simplified class:

#[derive(GodotClass, Debug)]
#[class(base = Node)]
pub struct AppState<'b> {
    #[base]
    base: Base<Node>,
    // some other fields
}

#[godot_api]
impl AppState {
    #[signal]
    fn paused();

    #[func]
    pub fn pause(&mut self) {
        // some other logic
        self.base.emit_signal("paused".into(), &[]);
    }
}

You might want to check that the logic in pause works and that emit_signal was called with paused as a parameter.

One way we could do this is by passing AppState a version of Base that stores what signals have been called:

#[cfg(test)]
mod tests {
    use super::*;

    // todo
    #[test]
    fn test_app_can_be_paused() {
        let base = InMemoryBase::new();
        {
            let mut app_state = AppState::new(&mut base);
            app_state.pause()
        }
        assert!(base.get_signals().contains("paused")); 
    }
}

I've tried a few of the suggestions for abstraction to allow my core logic to be tested but largely means writing a second struct with the same function names and then just having one call the other, meaning not only is it a lot of extra work to do this, but still leaves large parts of the code untestable, not to mention things like signals which can't be called from the other structs. For things like AppState it really doesn't make sense to have separate logic as all its doing is managing the changing of state and allowing other things to query the state so there's no way for me to test it.

I would love to help on this, but don't really know where to start in the codebase. It seems like we'd have to make a Trait around godot::engine::Object, then somehow allow Custom Class's to take other things that impl that trait without breaking the custom macro's ability to wire the struct.

I'm sure there are other ways to get the same results too.

Thoughts?

Metadata

Metadata

Assignees

No one assigned

    Labels

    c: registerRegister classes, functions and other symbols to GDScriptfeatureAdds functionality to the library

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions