There are still three more acceptance criteria to be implemented. The next in the list is:
"Given an investment, it should have the invested shares quantity."
Writing it should be as simple as the previous spec was. In the spec/InvestmentSpec.js
file, you can translate this new criterion into a new spec called should have the invested shares quantity
:
describe("Investment", function() { it("should be of a stock", function() { var stock = new Stock(); var investment = new Investment({ stock: stock, shares: 100 }); expect(investment.stock).toBe(stock); }); it("should have the invested shares quantity", function() { var stock = new Stock(); var investment = new Investment({ stock: stock, shares: 100 }); expect(investment.shares).toEqual(100); }); });
You can see that apart from having written the new spec, we have also refactored the call to the Investment
constructor, to support the new shares
parameter.
To do so, we used an object as a single parameter in the constructor, to simulate named parameters, a feature JavaScript doesn't have natively.
To implement this in the Investment
function is pretty simple: instead of having multiple parameters on the function declaration, it instead has only one, which is expected to be an object. Then, the function probes each of its expected parameters from this object, making the proper assignments:
function Investment (params) { var params = params || {}; this.stock = params.stock; };
The code is now refactored. We can run the tests to see that only the new spec should be failing:
To fix it, change the Investment
constructor to make the assignment to the shares
property:
function Investment (params) {
var params = params || {};
this.stock = params.stock;
this.shares = params.shares;
};
And finally everything is green:
But as you can see, the code that instantiates the Stock
and the Investment
is duplicated on both specs:
var stock = new Stock(); var investment = new Investment({ stock: stock, shares: 100 });
To eliminate this duplication, Jasmine provides another global function called beforeEach
that, as the name states, is executed once before each spec. So for these two specs, it will run twice—once before each spec.
Refactor the previous specs, by extracting the setup code with the use of beforeEach
:
describe("Investment", function() { var stock, investment; beforeEach(function() { stock = new Stock(); investment = new Investment({ stock: stock, shares: 100 }); }); it("should be of a stock", function() { expect(investment.stock).toBe(stock); }); it("should have the invested shares quantity", function() { expect(investment.shares).toEqual(100); }); });
Much cleaner, we not only removed the code duplication, but also simplified the specs. They became much easier to read and maintain, since their only responsibility now is to perform the expectation.
There is also a teardown function (afterEach
) that sets code to be executed after each spec. It is very useful in situations where a cleanup is required after each spec. We will see an example of its application in Chapter 6, Light Speed Unit Testing.
To finish the specification of Investment
, add the remaining two specs to the spec/InvestmentSpec.js
file:
describe("Investment", function() { var stock; var investment; beforeEach(function() { stock = new Stock(); investment = new Investment({ stock: stock, shares: 100, sharePrice: 20 }); }); it("should have the share payed price", function() { expect(investment.sharePrice).toEqual(20); }); it("should have a cost", function() { expect(investment.cost).toEqual(2000); }); });
Run the specs to see them failing:
Add the code to fix them in the src/Investment.js
file:
function Investment (params) { var params = params || {}; this.stock = params.stock; this.shares = params.shares; this.sharePrice = params.sharePrice; this.cost = this.shares * this.sharePrice; };
Run the specs for the last time to see them green: