There are three more acceptance criteria to be implemented. The next in the list is as follows:
"Given an investment, it should have the invested shares' quantity."
Writing it should be as simple as the previous spec was. In the InvestmentSpec.js
file inside the spec
folder, you can translate this new criterion into a new spec called should have the invested shares' quantity
, as follows:
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 changed 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.
Implementing this in the Investment
function is pretty simple—instead of having multiple parameters on the function declaration, it 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, as shown here:
function Investment (params) { this.stock = params.stock; };
The code is now refactored. We can run the tests to see that only the new spec fails, as shown here:
To fix this, change the Investment
constructor to make the assignment to the shares
property, as follows:
function Investment (params) {
this.stock = params.stock;
this.shares = params.shares;
};
Finally, everything on your screen is green:
But as you can see, the following code, which instantiates Stock
and 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 using the beforeEach
function:
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); }); });
This looks 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 fulfill the expectation.
There is also a teardown function (afterEach
) that sets the 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 InvestmentSpec.js
file, inside the spec
folder:
describe("Investment", function() { var stock; var investment; beforeEach(function() { stock = new Stock(); investment = new Investment({ stock: stock, shares: 100, sharePrice: 20 }); }); //... other specs it("should have the share paid price", function() { expect(investment.sharePrice).toEqual(20); }); it("should have a cost", function() { expect(investment.cost).toEqual(2000); }); });
Run the specs to see them fail, as shown in the following screenshot:
Add the following code to fix them in the Investment.js
file inside the src
folder:
function Investment (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 pass: