I have the following objects and relationships,
Lecture >- Tests
Test >- Questions
Business rules
When the lecture is started, a test can be given
If a test is being given, questions can be asked
Inference
Therefore questions shouldn't be asked if the lecture hasn't been started.
Question Model
class Question
belongs_to :test
belongs_to :lecture, :through => :test
def ask_question
raise "Test not started!" unless test.started?
raise "Lecture not started!" unless lecture.started?
end
end
So clearly the state of the question model is now coupled to the state of test and class.
When creating unit tests, to test this I need to set up all this state, which gets quite unwieldy, especially as the business cases get more and more complicated.
How can I avoid this?
I'm not experinced with Ruby associations, but it seems to me that somehow data model is cofused with run-time logic here.
If I'd make a data model for question and tests, I'd want to re-use my questions across tests and also re-use prepared tests (sets of questions) across lectures. In that case I'd write something like
class Lecture
has_and_belongs_to_many :tests
end
class Test
has_and_belongs_to_many :lectures
has_and_belongs_to_many :questions
end
class Question
has_and_belongs_to_many :tests
end
Separately from that structure I'd have some structure corresponding to real-time lectures, tests, questions and a notion of a result. A result is an attempt to answer a real-time question by a given student.
I'd also "delegate" the check of the lecture session state to the test session. If test session cannot be started for whatever reason the question session cannot be started too.
To unit-test a question session you will only need to mock a test session, to unit test a test session you will need to mock a lecture session, and so on.
class Lecture_Session
has_many :tests_sessions
belongs_to :lecture
end
class Test_Session
belongs_to :lecture_session
belongs_to :test
has_many :question_sessions
def validate
raise "Lecture not started!" unless lecture_session.started?
end
end
class Question_Session
belongs_to :question
belongs_to :test_session
def validate
raise "Test not started!" unless test_session.started?
end
end
class Result
belongs_to :lecture_session
belongs_to :test_session
belongs_to :question_session
belongs_to :student
def validate
raise "Question is not active!" unless question_session.active?
end
end
Hope this helps.