If a given test must be "frozen", then you should create a whole new test (questions, answers, etc) when the user wants to make changes. Rather than giving them an "edit" button on the question or answers, give them a "clone" button on the test.
The previous suggestion about marking the 'active' version is a good one.
EDIT:
I don't remember seeing your picture before, so I'll update based on it and your later comments. I would still suggest that you create a whole new test by "cloning" an existing one, but you could do that similarly to how your two *Log tables work, though with a little modification. How about this:
test (id), say one row.
question (id, text), 7 rows.
answer (id, text), 28 rows (4 for each question).
Note that there's nothing joining any of those together. Now...
test_question (test_id, question_id). 7 rows.
question_answer (test_id, question_id, answer_id). 28 rows.
Now, to change a question: issue a new test_id (+1 small row). Copy all the unchanged test_question and question_answer values for the new test (6+24 small rows). Add a new question row (+1) and copy the old question_answer to this new question (+4 small rows).
Most of your new rows are comprised of integers, so they'll be small and fast. You didn't copy any of the larger question/answer rows.
This also leaves you with the ability to find everything you need to know given just a test.id. With this type of versioning, you can say that Johnny took test #5, and know exactly what he saw, without any calculations, etc.
Hope that helps.
Best Answer
A fairly common method of creating a series in T-SQL consists of using a CTE as the source, something like:
The query above will return an ordered list of values from 0 to 99.
For dates, you can implement it like this:
Which provides a list of dates starting at January 1st, 2019, and continuing for 100 days, ending on April 10th, 2019.
There are numerous other ways of generating a set like this, including generating a "numbers" table.