I am working on a project where I am building on top of a solution of a very time-consuming problem. Instead of solving the very time consuming part many times, I only run the solver once, I save the solution with pickle, and then I reuse the pickle to test my features build on top of the solution.
Here is a model of what I mean
import time
import pickle
class Solution:
""" A class that represents a solution of a very time-consuming problem. """
def solve_difficult_problem() -> Solution:
time.sleep(1)
return Solution()
def prepare_solution() -> None:
""" I solve the problem only once. I use the solution for all the
development of my new feature as it never changes. """
solution = solve_difficult_problem()
with open('solved.pickle', 'wb') as file:
pickle.dump(solution, file)
def my_feature(solution: Solution) -> None:
""" I am using the solution. """
print(solution)
def test_my_feature() -> None:
# prepare_solution() # I run it only once: at the first run.
with open('solved.pickle', 'rb') as file:
solution = pickle.load(file)
assert my_feature(solution) is None
My problem is that with pytest, I need to manually uncomment the prepare_solution() at the first run of my tests. Is there a way for pytest to automate this?
I have tried writing a separate file which prepares the needed solution. But I don't know how to tell pytest that it should run it manually only the first time.
I would like the solution to be saved as a local file that persists longer than one session. Basically, I would like the solution to be available until I remove the whole project from my computer.
The fixture can run arbitrary code, so you can have it generate the pickle file on first use if needed.
@pytest.fixture
def solution() -> Solution:
pickle_path = Path("solved.pickle")
if pickle_path.exists():
with pickle_path.open("rb") as f:
return pickle.load(f)
print("Solving problem, please be patient")
solution = solve_difficult_problem()
with pickle_path.open("wb") as f:
pickle.dump(solution, f)
return solution
Depending on your needs, it may work to use a session-scoped fixture @pytest.fixture(scope="session"). This will cause the computation to happen only once when you run pytest, no matter how many tests run; on the other hand, it also causes the computation to happen every time you run pytest. This might make more sense if you're debugging the expensive algorithm and it's changing somewhat frequently.
It also may make sense to commit the pickle file to source control, and regenerate it out-of-band when required. This depends on internal details of your class, and so it would make sense only if the Solution class itself changes very very rarely.