Yeah, that’s basically what I do, but I often find I need to play around with intermediary data within functions.
class mymodel(): ... def feature_engineering(self): ... def impute_missing(self): ... def fit(self) ... def predict_proba(self) ...
Then it is pretty trivial to test with new data. And you can parameterize the things you want, e.g. init fit method as random forest or xgboost, or stack on different feature engineering, etc. And for debugging you can extract/step through the individual methods.
This is where standard SWE guidelines could be of help (function interfaces, contracts definition, etc)