Mine is a generic answer regardless of the language used.
Generally, when I have to test some method or function that has side effects or does not return any data, I check for some basic functions called within this function, and I mock them.
These core features are features that I assume are working and do not need to be further tested, such as:

Files management;
Access to the database;
etc..

I therefore suggest you find some libraries to allow you to make mocks of the services used within your functions and change the architecture of your software accordingly.
I hope I was clear.
My final project in CS50 is a salary slip generator in pdf format. I got these functions with me but I don't know to test them.

create_pdf() - function that opens my data file (.xlsx), iterates over its data, puts them into variables which will then be called by fpdf to put them into the pdf file. This function will generate as much pdf's depending on the number of data inside the data file.

merge_pdf() - function that merges all the previously generated pdf's into one pdf. This function I might try to check if it outputs the merged pdf or not but still not quite clear to me how to implement it.

get_print_date() - this function only I created just for the sake of adding extra functions to my project hoping that I can test it. It takes datetime.now() and returns the string value of the current date and time. But how can I assert also the return value if the return value changes over time?
Unit Testing Functions without Return Value in Python