Make an object-oriented quiz game using the following user stories:
As a quiz maker
So that I can challenge users
I want to be able to create a question with an answer
Expand - First User Story
The line we look at here is:
I want to be able to create a question with an answer
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer |
challenge
and answer
are set as properties, we have a Question
object so we'll describe a Question
constructor.
In a new file __tests__/question.test.js
, write the following code:
describe('constructor', () => {
it('has the given question property', () => {
const question1 = new Question();
expect(question1.challenge).toEqual('foo');
})
it('has the given answer property', () => {
const question1 = new Question(jest.fn(), 'bar');
expect(question1.answer).toEqual('bar');
})
})
In src/question.js
, add the following code:
function Question(challenge, answer) {
this.challenge = challenge;
this.answer = answer;
}
As a player
So I know if my guess is correct
I want to be able to verify my answer to a question
Expand - Second User Story
The line we look at here is:
I want to be able to verify my answer to a question
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer | ||
verify |
verify
is a method, added to Question
constructor.
In a new file __tests__/question.test.js
, write the following code:
describe('verify', () => {
it('returns true if the answer matches', () => {
const question = new Question(jest.fn(), 'bar');
expect(question.verify('bar')).toBe(true);
});
})
In src/question.js
, add the following code:
Question.prototype.verify = function verify(guess) {
return guess === this.answer;
}
As a quiz maker
So I can group related questions together
I want to be able to create a quiz with a list of questions
Expand - Third User Story
The line we look at here is:
I want to be able to create a quiz with a list of questions
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer | ||
verify | ||
Quiz | questions |
questions
is assumed as a property, we have a quiz
object so we'll describe a quiz
constructor.
In a new file __tests__/quiz.test.js
, write the following code:
describe('quiz constructor', () => {
it('has the given list of questions', () => {
const questions = [jest.fn(), jest.fn()];
const quiz = new Quiz(questions);
expect(quiz.questions).toEqual(questions);
});
})
Alternative unit test:
describe('quiz constructor', () => {
it('has the given list of questions', () => {
const q1 = jest.fn();
const q2 = jest.fn();
const quiz = new Quiz(questions);
expect(quiz.questions).toEqual(q1, q2);
});
})
In src/quiz.js
, add the following code:
function Quiz(questions) {
this.questions = questions;
}
As a quiz maker
So I can keep track of my quizzes
I want to provide a quiz with a name/identifier
Expand - Fourth User Story
The line we look at here is:
I want to provide a quiz with a name/identifier
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer | ||
verify | ||
Quiz | questions | |
name |
name
is a property, added to Quiz
constructor.
As a player
So I know what question to answer
I want to be able to read the current question
Expand - Fifth User Story
The line we look at here is:
I want to be able to read the current question
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer | ||
verify | ||
Quiz | questions | |
readCurrentQuestion |
readCurrentQuestion
is a method, added to Quiz
constructor.
In a new file __tests__/quiz.test.js
, write the following code:
describe('readCurrentQuestion', () => {
it('returns the challege of the current question', () => {
const mockChallenge = jest.fn();
const questions = [{ challege: mockChallenge }];
const quiz = new Quiz(questions);
expect(quiz.readCurrentQuestion()).toEqual(mockChallenge);
});
})
In src/quiz.js
, add the following code:
Quiz.prototype.readCurrentQuestion = function readCurrentQuestion() {
return this.questions[0].challenge;
}
As a player
So I can play the game
I want to be able to provide an answer to the current question
Expand - Sixth User Story
The line we look at here is:
I want to be able to provide an answer to the current question
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer | ||
verify | ||
Quiz | questions | |
readCurrentQuestion | ||
verifyCurrentQuestion |
verifyCurrentQuestion
is a method, added to Quiz
constructor.
In a new file __tests__/quiz.test.js
, write the following code:
describe('verifyCurrentQuestion', () => {
it('verifies the guess against the current question', () => {
const mockGuess = jest.fn();
const mockVerifyValue = jest.fn();
const mockQuestion = {
verify: jest.fn(() => mockVerifyValue),
};
const questions = [mockQuestion];
const quiz = new Quiz(questions);
const result = quiz.verifyCurrentQuestion(mockGuess);
expect(result).toEqual(mockVerifyValue);
expect(mockQuestion.verify).toHaveBeenCalledWith(mockGuess);
});
})
In src/quiz.js
, add the following code:
Quiz.prototype.verifyCurrentQuestion = function verifyCurrentQuestion(guess) {
return this.questions[0].verify(guess);
}
As a player
So I can progress through a game
I want to move onto the next question after I provide an answer to a question
Expand - Seventh User Story
The line we look at here is:
I want to move onto the next question after I provide an answer to a question
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer | ||
verify | ||
Quiz | questions | |
readCurrentQuestion | ||
verifyCurrentQuestion | ||
nextQuestion | ||
quizIndex |
nextQuestion
is a method, quizIndex
is a property, added to Quiz
constructor.
In a new file __tests__/quiz.test.js
, write the following code:
describe('nextQuestion', () => {
it('move to next question from Q.1', () => {
const questions = [jest.fn(), jest.fn()];
const quiz = new Quiz(questions);
quiz.nextQuestion();
expect(quiz.quizIndex).toEqual(2);
});
});
In src/quiz.js
, add the following property quizIndex
with initial value to constructor, and method nextQuestion
:
function Quiz(questions) {
this.questions = questions;
// start from array[1] to match first question (Q1).
this.quizIndex = 1;
}
Quiz.prototype.nextQuestion = function nextQuestion() {
// this.quizIndex ++;
this.quizIndex += 1;
};
As a player
So I can keep track of how I'm doing
I want the game to track my score
Expand - Eighth User Story
The line we look at here is:
I want the game to track my score
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer | ||
verify | ||
Quiz | questions | |
readCurrentQuestion | ||
verifyCurrentQuestion | ||
score | ||
nextQuestion | ||
quizIndex |
Already got verifyCurrentQuestion
which will need to be extended to include score
as a property.
In a new file __tests__/quiz.test.js
, write the following code:
describe('verifyCurrentQuestion', () => {
it('increase score if return true', () => {
const mockQuestion = {
verify: jest.fn(() => true),
};
const questions = [mockQuestion];
const quiz = new Quiz(questions);
quiz.verifyCurrentQuestion(jest.fn());
expect(quiz.score).toEqual(1);
expect(quiz.score).toBeTruthy();
});
In src/quiz.js
, add the following property score
with initial value to constructor:
function Quiz(questions) {
this.questions = questions;
this.quizIndex = 1;
this.score = 0;
}
Quiz.prototype.verifyCurrentQuestion = function verifyCurrentQuestion(guess) {
if (this.questions[0].verify(guess)) {
this.score += 1;
}
return this.questions[0].verify(guess);
};
As a player
So I can know when I have answered all of the questions
I want to be told that a quiz is finished when trying to read or answer a question
Expand - Ninth User Story
The line we look at here is:
I want to be told that a quiz is finished when trying to read or answer a question
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer | ||
verify | ||
Quiz | questions | |
readCurrentQuestion | ||
isFinished | ||
verifyCurrentQuestion | ||
score | ||
isFinished | ||
nextQuestion | ||
quizIndex |
Need to include isFinished
as a property for methods readCurrentQuestion
and verifyCurrentQuestion
.
Property isFinished
is a function based on quizIndex
value where the Quiz is completed once it reaches the last array, with the modification applied to the readCurrentQuestion
and verifyCurrentQuestion
functions. Much simiplar method would be to use the getter methods which will require one object to cover the two modification mentioned.
What a getter methods is... Getter methods allow access to a property that returns a dynamically computed value - they are methods that can be used like properties - they allow us to report on an objects state, without actually managing a new piece of state. Meaning, the less state you have to manage, the simpler your code will be.
When you create a method, Quiz.prototype is an object, and you are setting a property on that object. However for getter methods, they have to be created on an object literal in src/quiz.js
:
Quiz.prototype = {
get isFinished() {
return this.quizIndex >= this.questions.length;;
}
}
Implementing a unit test for testing one of the condition for quiz.isFinished
getter method in __tests__/quiz.test.js
:
describe('readCurrentQuestion', () => {
it('throws an error when game is finished', () => {
const mockChallenge = jest.fn();
const questions = [{ challenge: mockChallenge }];
const quiz = new Quiz(questions);
quiz.quizIndex = 11;
expect(quiz.readCurrentQuestion()).toEqual(false);
});
});
Note; there are ten questions in the index.js
file, therefore quizIndex = 10
.
Updated code added to objects readCurrentQuestion
and verifyCurrentQuestion
:
Quiz.prototype.readCurrentQuestion = function readCurrentQuestion() {
if (!this.isFinished) throw new Error('Time Up!');
return this.questions[this.quizIndex].challenge;
};
Quiz.prototype.verifyCurrentQuestion = function verifyCurrentQuestion(guess) {
if (!this.isFinished) throw new Error('Time Up!');
else if (this.questions[this.quizIndex].verify(guess)) this.score += 1;
return this.questions[this.quizIndex].verify(guess);
};
As a player
So I can keep track of my achievements
I want my high scores for each quiz to be recorded on my profile after completing it
Expand - Tenth User Story
The line we look at here is:
I want my high scores for each quiz to be recorded on my profile after completing it
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer | ||
verify | ||
Quiz | questions | |
readCurrentQuestion | ||
isFinished | ||
verifyCurrentQuestion | ||
score | ||
isFinished | ||
nextQuestion | ||
quizIndex | ||
highScore | ||
finalScore |
Need to include highScore
as a property for the constructor Quiz
.
Implementing a unit test for testing the condition for quiz.highScore
in __tests__/quiz.test.js
:
describe('highScore', () => {
it('high scores recorded after each completion', () => {
const quiz = new Quiz(jest.fn());
quiz.score = 6;
expect(quiz.highScore()).toEqual('Congrats! You have a new high score!');
});
});
Updated code added to include property quiz.finalScore
, method quiz.highScore
, and a magic number:
const TOP_SCORE = 5;
function Quiz(questions) {
this.questions = questions;
this.quizIndex = 0;
this.score = 0;
this.finalScore = TOP_SCORE;
}
Quiz.prototype.highScore = function highScore() {
if (this.score > this.finalScore) {
this.finalScore = this.score;
return ('Congrats! You have a new high score!');
} return ('Try again. High score not beaten');
};
As a player
So I can brag to my friends when I do the best
I want new high scores for a quiz to be recorded along with my name
Expand - Eleventh User Story
The line we look at here is:
I want new high scores for a quiz to be recorded along with my name
This could be translated to:
Object | Methods | Properties |
---|---|---|
Question | challenge | |
answer | ||
verify | ||
Quiz | questions | |
readCurrentQuestion | ||
isFinished | ||
verifyCurrentQuestion | ||
score | ||
isFinished | ||
nextQuestion | ||
quizIndex | ||
finalScore | ||
highScore | ||
finalResult | ||
name |
Need to include finalResult
as a method for the object Quiz
.
Implementing a unit test for testing the condition for quiz.finalResult
in __tests__/quiz.test.js
:
describe('finalResult', () => {
it('display new high scores along with name', () => {
const quiz = new Quiz(jest.fn());
quiz.name = 'Bruce Forsyth';
quiz.score = 10;
expect(quiz.finalResult()).toEqual('Hi Bruce Forsyth your final score is 10');
});
});
Updated code added to include new method quiz.finalResult
:
Quiz.prototype.finalResult = function finalResult() {
return (`Hi ${this.name} your final score is ${this.score}`);
};