In this video, I will show you how to
create a package, a class, and describe tests.
The first thing to do is to open the system browser.
The system browser enables to browse packages.
Here there are packages.
The first thing to do is to create a package.
We will click on “Add package”, "MyCounter".
As there are many packages and we don’t want
to see them, it is a good thing to filter this.
We have our package "MyCounter".
And there, we define a class that we call
"Counter" and it has an instance variable “count”.
The little orange icon means that the code has not been saved.
Here, we will compile code with the “Accept” function.
And here, the class "Counter" has been compiled.
The system searches a little and says that there are
good comments. We will see this later. For the moment, we don’t care.
There is a red exclamation mark because we haven’t written
a comment. So, we will write a first
comment:
"I'm a simple counter…”
We write a little example so that the counter understands
how it will work: Counter new,
Counter increment, increment, Counter
count. Here it should be equal to 2.
Once more, the text hasn’t been saved and we will compile it.
It has been saved. We don’t have a counter anymore.
Now, we are going to define accessors.
We generally put accessors in the Accessing protocol.
We write: "count returns count".
In the same way, we write
aNumber, we do a Setter.
You see, I modified the count method.
You can think that it has been lost, but no, because
each time I change a method using a different name,
it creates a new method.
I compile the new method, I have “count”.
Now, we really want to test and write our first little program.
I open Playground.
I create a counter, we go on another line.
I put a value:
C count 7. If I ask for its value, what will it
return? It will return 7.
Now, what we’d really like to write
this as a test because like this we can’t execute it
and we’d like to execute it automatically.
We create a test class.
How do we create a test class in Pharo?
We change the super class, we say it inherits this
from TestCase and we will define a test class
called CounterTest.
Now, I will compile it.
I have a new CounterTest class in my system.
I will define a new method, I will add
a protocol called tests.
There I can define a new method.
Tests methods must always start by “test”
and be in a class that inherits from TestCase.
We will call it "testCounterisSetAndRead"
for instance. We almost have our test. We have already
almost written everything.
We will indent it a little better.
I create my counter.
And now, I write "self assert
equals 7". This sentence is
strictly equivalent to "self assert C count equals 7".
It is more convenient, when you make a mistake
the return is smarter when you use the first sentence.
We will compile.
Now, I can make my test executed,
my test is green. When you have a test and it is green,
it is the right time to save your production, your code.
In Pharo, you can save the image.
Here, that’s what I do, I will save the image, but the image
is not satisfactory..
I’d like to show you how you can save code
in a versions system.
Here, as I don’t know if you will have an Internet access,
I will do it in my local cache.
You see, I have this directory pointing toward
all those packages on which I can work
and that I have loaded or not.
There, the system shows us that
several packages with modifications haven’t been saved.
We don’t care about the first 2, but we see that
MyPackage has a star. It means there is code in
the image that hasn’t been saved.
Here that’s what I’m going to do: I click on Save,
I choose the repository.
I click on save.
I write "first version with accessors and a
green test".
I accept. Now it has saved one package.
We will check.
I can open this directory. If I look, I contains
lots of packages that have been loaded by the system.
But in particular, if I filter, I find
my package with its versions.
I advice you to save,each time you add
a feature, and that’s what we will do.
Now we will define
a test before implementing it.
We will write a test for what is the increment
of a
counter. "TestIncrement" What do we want to obtain?
We want to say it “I create
a counter,
I assign it a default value,
then I increment twice this counter.
“What should I check?”
I should check that the counter
has the correct value.
I compile. My test
is red as I didn’t implement counter.
The operations in counter increment and decrement.
I add "operations increment".
What will we do?
We do "self count 2: self count +
1". If we use systematically accessors
internally or not, it depends, it is a question of style.
in the same way, as it is very close,
we will define "decrement".
You see that on increment, there is a test so I can
execute it and my test is green.
So I do have 2 tests that work well.
If I go in the test class, all my tests are green.
What am I going to do?
I save. I will make a new save.
I will write "Added increment and decrement".
Now if I look, I have 2 versions
of my packages.
We will write a test for "decrement"
because there’s no reason not to do it.
"Decrement", for instance I
could attribute the value 2 if I want
and I decrement twice. I should get 0.
My test is green so
I will save.
Now, what we want is if I look
and I write "Counter new" and then
"Print it", it displays a counter which value I don’t know,
so it is not very useful.
We would like to modify this behaviour.
To do this, we will add a method
called ”PrintOn", in the Printing protocol.
Print on is a method called
by the system each time you do PrintStream.
It has aStream as an argument.
What do we do?
We say that we’d like to have a counter.
Here typically, I use the above functions.
Now what I will do, I’d like
to add a little
text
for instance "with value" and "self count".
I do this because it is a number, so I have
to get a textual representation.
I write "cr".
I compile.
The system tells me that I could do better, but no matter…
For now we don’t care.
I can write "Counter new".
I look, it says that it is a counter with a nil value.
That’s normal because I haven’t initialised it yet.
Now we will add a support to the initialisation.
To do this, we begin to write a test.
I say: “At the creation, I want the values
to be 0.
What will I do?
I will write “self asser”
I check that "Counter new
count = 0".
There, what do we notice?
My test is false. Why?
Because if I do this, I get nil.
As it is a class and I don’t get access to
instance variables of the CounterTest class,
I can select the expression and execute it directly.
Once I have this test that isn’t valid,
what should I do?
I will add an initialize method in the initialisation protocol.
And there what do I do?
I write "super initialize", and I will
say "count 2 points = 0".
This method is used each time I add the “new” method.
Now I can look at my tests, it’s green.
In the same way, if I write "count", I get 0.
Once more, we will save: "added printOn"
"added initialize + tests".
Now what is interesting is that
I can see all my history.
I can also see and browse this history.
If I click on “version”, I can see the difference between these 2 systems.
Changes between version 3 and version 4
are ”initialize" "printOn" and the Test.
If I want the difference between
version 2 and version 4,
I have all these changes.
Now, I remains one thing to do, I’d like
to change a little the way objects are created.
I start off by creating a test that will clarify
what I want to get. I will define a test
called "AlternateClassCreation".
What do we want to obtain?
Let's say, I want to be sure that
that Counter withValue: 19
If I do increment on it
and I ask for its value, it must return 20,
Ok.
Let's add a parenthesis here, it's better.
So, once more, the test isn't ok,
and the assistant tells me: "you send an unknown message".
You must notice that the message withValue
is no more sent to an object but to a class.
It means I have to define this message
on a class. So I select the class.
Now I go to class level and I add
a protocol called instance creation
withValue: aNumber So what do I have to do?
The first thing to do is
to create a counter, a counter variable,
counter, I do it slowly,
self new So I create a counter
and I write self count: aNumber
and I return this counter.
We can do it in a shorter way
but for the moment let's do it simply.
I compile my method.
And now it means I can send messages.
You see, when I click on this button,
I go on the class level. I go from the class level je passe au niveau classe. Donc je passe du niveau classe
to the instance level.
Here I was on the class level and here I go back on
the messages sent to instances.
What does it means? It means that if I do
Counter withValue: 18, what will I get?
I will get an instance of the counter class.
I get a counter with value 18.
And here I could do decrement.
If I refresh, I do get 17.
Know if I look at my test,
all my tests are ok, so I will once more
save and I'm done.
Added instance creation API and tests.
Done for now.