I advanced a little in my code but I find myself facing another problem for two days. I would like to generate a test method using only the source code. But I have no idea how to do it.
I have a method that allows me to build the name of a test method but I can't write in it.
buildSelectorFor: aMethod
^ String streamContents: [:i || capitalize |
capitalize := true.
i << 'test'.
aMethod selector do: [:charactar |
charactar= $:
ifTrue: [ capitalize := true ]
ifFalse: [ capitalize
ifTrue: [
capitalize := false.
i << charactar asUppercase. ]
ifFalse:[ i << charactar ]]]]
so if I execute this method with this for example:
buildSelectorFor:Car>>#speed:mark:
I get this:
testSpeedMark
my goal is to get something like
testSpeedMark
self assert:....equals:...
I added a method writeTestMethod.
writeTestMethod: aMethod with: anObject
^(self buildTestSelectorFor: aMethod),'
|classMethod setter instObject method|
classMethod := aMethod methodClass.
setter := (classMethod allSelectorsInProtocol: #setter) asArray.
instObject := classMethod new.
(setter with: anObject do: [:set :ivar | instObject perform: set with: ivar]).
self assert: instObject class equals: (classMethod new) class.'
So here is what I get:
I don't know how to integrate the parameters of writetestMethod in the code I want to generate
Related
Here is a description of my problem. I have a Person class which has three attributes: lastname, name and birthDate.
Object subclass: #Person
instanceVariableNames: 'name lastName birthDate'
classVariableNames: ''
package: 'Moi'
i have the setters:
name: aString
name := aString
and similarly for birthDate and lastName.
For this class, i have a constructor:
Person class >> withName: aName birthDate: aDate lastName: aLastName
| person |
person := self new.
person
name: aName;
birthDate: aDate;
lastName: aLastName.
^person
To create an instance of a class i send new to the class:
person := Person new
Then, i provide values to its ivars:
person name: 'toto'; birthDate: '13 Sep 2022'; lastName: 'tata'
Now, I'm not going to have the user enter the method values himself
Person>>#withName:andBirthDate:andLastName:
For this I wrote a method generateData which takes between a method and generates the values that the method receives as arguments. i call it like this in my playground:
generateData:Person>>#withName:andBirthDate:andLastName:
once inside the method, I start by retrieving the instance variables of the class via:
iVars := aMethod variableWriteNodes.
(iVars collect: [ :i | myAllInstVars add:i name ]).
at the end, myAllInstVars has all the instance variables of the class where the method has been implemented. now i am generating random value for each variable based on its type. to do it, i do this:
resultTypeVariables collect: [ :i |
(i isFloat ) ifTrue: [ items add: ((1 to: 1000) atRandom asFloat) ].
(i = SmallInteger) ifTrue: [ items add:(1 to: 256) atRandom ].
(i isInteger) ifTrue: [ items add:(1 to: 256) atRandom ].
(i isNumber) ifTrue: [ items add:(1 to: 256) atRandom ].
(i isString ) ifTrue: [ items add:UUID new asString36].
(i == ByteString ) ifTrue: [ items add:UUID new asString36].
(i == Date) ifTrue: [ items add:(Date fromDays: (1 to: 36000)atRandom) ].
].
items contains the generated values.
This is where my problem begins.
I would like to rebuild the Person>>#withName:andBirthDate:andLastName: method by adding in its signature the values contained in items.
here is the idea i implemented. I retrieve the setters in the setter protocol like this:
setter := classMethod allSelectorsInProtocol: #'setter'.
( setter ) do: [:i|
instObject := setter,':',items.
].
but when i return instObject i get this as result:
I don't know what to do right now.
I think that the part you are missing here is the #perform: family of messages. They transform selectors into messages as follows
person := Person new.
person perform: #name: withArgument: 'toto'
where #perform:with: builds the message with selector #name: and argument 'toto'. While there are variants for any number of arguments, what you need is the one I just described.
Thus, if you have say ivars := #('toto' '12 Sep 2022' 'tata') you will be done with
setters with: ivars do: [:setter :ivar | person perform: setter with: ivar]
where setters := #(#name: #birthDate: #lastName:).
Given that in your case #allSelectorsInProtocol: collects the selectors in a Set, you might want to put them in an Array instead and sort them alphabetically for indentification:
(class allSelectorsInProtocol: #setters) asArray sorted
which will produce #(#birthDate: #lastName: #name:). Note also that this will require collecting your data in the same order so to match the arguments.
I have a scenario where a class holds two instance variables that are mutually exclusive. That is only one can be instantiated at a time. To be precise, I have a Promise class (trying to add promises to Pharo) and it holds promiseError and promiseValue instance variables. I then want to implement the method "then: catch:".
This method should work as follows:
promiseObject := [10/0] promiseValue.
promiseObject then : [ : result | Transcript crShow : result ]
catch : [ : failure | Transcript crShow : failure ] .
I got an idea on how to implement methods that take a block as an argument from method that accepts a block and the block accepts an argument.
My attempt below will obviously not work but I have no idea on how to make it work.
then:aBlock catch: anotherBlock
|segment|
promiseValue ifNil: [ segment := promiseError ] ifNotNil: [ segment := promiseValue ].
promiseValue ifNil: [ segment := promiseValue ] ifNotNil: [ segment := promiseError ].
aBlock value:segment.
anotherBlock value: segment
This should work analogously to a try-catch block.
Have you tried something like this?
then: aBlock catch: anotherBlock
promiseError notNil ifTrue: [^anotherBlock value: promiseError].
^aBlock value: promiseValue
Note that the code does not rely on promiseValue being nil or not because nil could be a valid answer of the promise. However, if there is some promiseError, we know the promise failed, and succeeded otherwise.
Of course, here I'm assuming that this message will get sent once the promise has been successfully or unsuccessfully finished. If this is not the case, then the code should be waiting on the promise semaphore.
I want to create a method that gets a block as an argument, and the block gets a parameter as well.
If the block returns true it should do something ( for example return 1), and if it returns false it should do something else.
this is what I did.. but I am getting syntax error on the ifTrue...
is this the way I should get as a parameter a block that receives an argument?
Mymethod: Block
Block value: 'argument'
ifTrue: [ ^1].
ifFalse: [^2].
and the call to the method :
object := myClass new.
argument :=1
boolValue := object Mymethod : [:argument | argument ==1 ]
the way you wrote it means that #value:ifTrue: message to the Block, and then you are sending #ifFalse: message to nothing (which is not possible at all. If you want to do it in one line, you should use parenthesis:
(Block value: 'argument')
ifTrue: [ ^1]
ifFalse: [^2]
Also in smalltalk it's a convention to name variables with uncapitalized, like block or aBlock
The return value of #value: message, when sent to a block, is the value of the last sentence in that block. So [ 1 + 2. 3 + 4. ] value evaluates to 7.
I find that hard to use sometimes. Is there a way to explicitly set the returning value and stop executing the block?
For exercise, try rewriting this block without using my imaginary #return: message and see how ugly it gets. I must be missing something.
[ :one :two |
one isNil ifTrue: [ two isNil ifTrue: [ self return: nil ] ifFalse: [ self return: true ] ].
two ifNil: [ self return: false ].
(one > two)
ifTrue: [ self return: true ]
ifFalse: [ (one < two)
ifTrue: [ self return: false ]
ifFalse: [ self return: nil ]
].
]
EDIT: self return: sth really is nonsense, but it does make sense at some level :)
There's nothing like a guard clause - blah ifTrue: [^ foo] - inside a block, because ^ is a non-local return, returning from the method calling the block rather than the block itself.
Big blocks - like big anythings - should be refactored into smaller, more understandable/tractable subparts, but sometimes that's not always possible. I mean this answer to suggest options to try when you can't really simplify in the usual ways.
If your block is really that complicated, and you can't get it simpler (splitting it up delocalises the information too much, for instance) then perhaps you can use an explicit return value. In particular, if your block doesn't return nil you could do something like
[:one :two | | result |
result := (one isNil and: [two isNil]) ifTrue: [false].
result ifNil: ["do one thing, possibly setting result"].
result]
If your block can return nil, you'll need another sentinel value:
[:one :two | | result marker |
result := marker := Object new.
(result == marker) ifTrue: ["do one thing, possibly setting result"].
result]
Lastly - and I hesitate to suggest this - you could do this:
[1 + 2.
thisContext return: 5.
3 + 4] value
which returns 5.
(Verifying how this interacts with ^ and inlined selectors like #ifTrue:ifFalse: left as an exercise for the reader.)
It seems that your code tries to handles nil like an infinity value when comparing one and two. The following code may be more readable depending on the context:
a := [:one :two |
| x y |
x := one ifNil: [Float infinity].
y := two ifNil: [Float infinity].
(x = y) ifTrue: [nil] ifFalse: [x > y]]
A useful feature of #ifTrue:ifFalse:, #ifNil:ifNotNil: and similar testing methods is that they return the value of the block that gets evaluated. e.g. (4 > 1) ifTrue: ['greater'] ifFalse: ['not-greater'] evaluates to 'greater'. This feature often makes it possible to return a value from a nested block in tail position.
When the code inside a block gets too complicated I suggest your refactor it to a method. But see Frank's answer for workarounds.
Edit:
As pointed out in the comments the code above assumes numbers. I also came up with something that works with other comparable objects:
a:=
[ :one :two |
true caseOf: {
[one = two]->[nil].
[one isNil]->[true].
[two isNil]->[false]
} otherwise: [one>two]]
That #caseOf: construct is rarely used but it's certainly better than thisContext return:
You'd like to implement some break, continue, exit...
The usual way to control flow in Smalltalk is with blocks.
So one funny solution is to use a helper method with a Block return value to break the flow, like described here .
Object>>exitThru: aBlock
^aBlock value: [:result | ^result]
Now, let see how to use it:
| aBlock |
aBlock := [ :one :two |
self exitThru: [:exit |
one isNil ifTrue: [ two isNil ifTrue: [exit value: nil ] ifFalse: [ exit value: true ] ].
two isNil ifTrue: [ exit value: false ].
one > two ifTrue: [ exit value: true ].
one < two ifTrue: [ exit value: false ].
exit value: nil] ].
#(('abc' nil) (nil nil) (nil 'def') ('y' 'abc') ('y' 'y') ('y' 'z'))
collect:
[:pair |
aBlock value: pair first value: pair last ]
-> #(false nil true true nil false)
EDIT my first version was unnecessarily complex, can't remember what lead me to an additional indirection:
| aBlock |
aBlock := [:wrapOne :wrapTwo |
self exitThru: [:exit |
[ :one :two |
one isNil ifTrue: [ two isNil ifTrue: [exit value: nil ] ifFalse: [ exit value: true ] ].
two isNil ifTrue: [ exit value: false ].
one > two ifTrue: [ exit value: true ].
one < two ifTrue: [ exit value: false ].
exit value: nil ]
value: wrapOne value: wrapTwo ] ].
Well, more funny than usefull, I hope you will find more simple and expressive way to code.
Tryng out some smalltalk + TDD + "good practices" I've run into a kinda ugly block:
How do I do an assertion in GNU Smalltalk?
I'm just looking for a simple ifFalse: [Die] kind of thing
This is the code for assert: from Squeak (which I recommend you use rather than GNU):
assert: aBlock
"Throw an assertion error if aBlock does not evaluates to true."
aBlock value
ifFalse: [AssertionFailure signal: 'Assertion failed']
as well as
self assert: [ ... some block ]
works for blocks & non-blocks, since sending #value to Object returns self.
It has been suggested above to add #assert: to Object, but rather I'd add #assert to BlockClosure (or whatever [] class is in GNU Smalltalk).
assert
this value ifFalse: [AssertionFailure signal: 'Assertion failed']
and thus use as in
[ value notNil ] assert.
[ value > 0 ] assert.
[ list isEmpty not ] assert.
etcetera.
It is simple. In your test methods you write:
self assert: 1 + 1 = 2
But first you need to create a test class as a subclass of TestCase (in Squeak), for example:
TestCase subclass: #MyTest
Here you write testing methods, which names must always start with 'test', for instance :
testBasicArithmetics
self assert: 1 + 1 = 2