Diamond Kata using property-based tests with Nat Pryce
Hello All,
In case anyone is interested, here is a video in which Nat Pryce and I are doing a diamond kata at KotlinConf
Regards, Dmitry
|
Re: "I think the community needs more explicit direction...."
Right!
I have found teaching a team right when (or immediately after) they run into an applicable problem is most effective and rewarding - in other words, active, hands-on coaching.
After years of teaching I grew really frustrated with seeing that the students did not really get what I was teaching, even if they could de well on a test on it.
toggle quoted message
Show quoted text
Are we trying to "educate" too quickly?
Indeed!?
We often get taught the solution before even experiencing the problem. That's coming from my experience in Belgium.
Where I got my drive to learn about more-than-just-working-code was from experiencing the problem first.
I rewrote the same pet-project multiple times, never able to finish it. The code grew too complex, and if I paused and revisited after a couple of weeks/months it was exhausting just to get up to speed. The first 2 or 3 tries I did not know about Version Control, The last version I tried to solve by using Design Patterns, UML designs on paper. It still grew too complex, and I paused/dropped it again. Even at the end I still had no notion of documentation, clean code, software design, tests (let alone TDD).
This made me receptive to the thoughts like:
- we need more than working-code to succeed.
- a rewrite will probably not solve the underlying problem.
Which problems is this community trying to solve? How could we get people to experience these problems?
I believe that is the optimal first step in learning.
1) Did they experience the problem?
?
2) Do they see it as a problem?
? ? aka Do they have a problem with it? ? ? aka Do they care?
?
3) Do they attribute the problem to the right cause?
?
4) Do they believe they could solve the problem?
? ? or do they feel powerless to change it,
? ? perhaps??like
- blame (someone else needs to change)
- justify (the world needs to change)
- shame (I can't change myself)
- obligation (I should, but don't want to)
5) Do they believe that you can help them in solving their problem?
|
Re: "I think the community needs more explicit direction...."
Are we trying to "educate" too quickly?
Indeed!?
We often get taught the solution before even experiencing the problem. That's coming from my experience in Belgium.
Where I got my drive to learn about more-than-just-working-code was from experiencing the problem first.
I rewrote the same pet-project multiple times, never able to finish it. The code grew too complex, and if I paused and revisited after a couple of weeks/months it was exhausting just to get up to speed. The first 2 or 3 tries I did not know about Version Control, The last version I tried to solve by using Design Patterns, UML designs on paper. It still grew too complex, and I paused/dropped it again. Even at the end I still had no notion of documentation, clean code, software design, tests (let alone TDD).
This made me receptive to the thoughts like:
- we need more than working-code to succeed.
- a rewrite will probably not solve the underlying problem.
Which problems is this community trying to solve? How could we get people to experience these problems? I believe that is the optimal first step in learning.
1) Did they experience the problem?
?
2) Do they see it as a problem?
? ? aka Do they have a problem with it? ? ? aka Do they care?
?
3) Do they attribute the problem to the right cause?
?
4) Do they believe they could solve the problem?
? ? or do they feel powerless to change it,
? ? perhaps??like
- blame (someone else needs to change)
- justify (the world needs to change)
- shame (I can't change myself)
- obligation (I should, but don't want to)
5) Do they believe that you can help them in solving their problem?
|
Re: We are back on the air!
I also like the diagram in GOOS, page 42, where the authors add an additional step, right after "write a failing test": "Make the diagnostics clear."
toggle quoted message
Show quoted text
On Mon, Nov 25, 2019 at 4:11 AM Roy Osherove < roy@...> wrote: Yup!
On Thu, Nov 14, 2019 at 6:05 AM Avi Kessner < akessner@...> wrote: Is this the diagram you wanted to share?
I would recommend making a box which is part of the flow rather than some external dotted lines. For example, maybe the box should read, " Apply SOLID principles" as part of the refactoring, and then before the next failing test have something like, "design the contract"
I think the community needs more explicit direction on the design being done in those phases.
On Tue, Nov 12, 2019 at 11:27 AM Roy Osherove < roy@...> wrote: ? Awesome.? I sent a new thread to the group about tdd diagrams.? Was it received?
Maybe.
If you sent it to the Yahoo! group, then no.
Please try again--at least until we figure out the transition. :) I want to see it! -- J. B. (Joe) Rainsberger :: :: ::
--
|
Re: "bottom-up" TDD and common behaviors
I’m saying spending the time upfront/real-time to make the code maintainable is faster and cheaper in the long term. Coupling directly to anything outside of the business logic -> that I found more expensive, even if we stay with the same
‘framework’, which happens to be upgraded to the next version.
?
Business domain language: ok, I think you’re saying “updating an employee” is better than “storing an employee”. I buy that. I just find testing `employee.update()` harder because of the coupling. What’s a good way of testing this then?
?
Faster and cheaper to program does not mean easier to maintain, especially if the long term owner of the code is maintaining dozens of applications written by different teams with different ideas about what is faster, cheaper, efficient
and expressive.? When a framework is chosen, there are reasons, which may indeed include assumptions about the long term maintenance cost of lots of applications.? Ask why in case respecting the choice of framework is more in the customer's long term interest
than abstracting the framework into your way of coding.
In XP, we endeavor to write logic in the business domain language instead of the technical language of bits, bytes, programatic structures, data structures, database records, datastores, etc.??
?
toggle quoted message
Show quoted text
On Thu, Dec 5, 2019 at 7:00 PM Daniel Olewski < dolewski@...> wrote:
In my experience the separation of application logic and externals is much faster and cheaper than most think. Even more including any long-term benefits when things change. It’s
just one of the code design activities which we do anyways. Pays off in weeks – not years … .
?
`store-record()` to me implies storing in a single db record, which leaks ‘db’ term into this surface.
`employee_storage.store(employee)` is more general – don’t care if it’s storing in a record, many records or somewhere else. That’s why I prefer the latter.
?
employee.store() – this seems to imply that the employee needs to know how to store/retrieve itself? That’s a coupling I’d rather avoid – I’d rather couple to a shape of data (Employee)
and pass it around freely across classes/processes/languages/operating system/computers … . Employee would typically already have a single responsibility of keeping itself valid (what’s required/optional, valid values, etc.) – why add another responsibility
and more coupling?
?
We should listen to our paying customers who may have some thoughts on the future of their company and their application.? They may not want to pay for the extra time and effort
to abstract a framework?that they may have chosen for a specific business reason,? They?may prefer to be maintaining a family of applications that leverage the same framework in the obvious direct way instead of a family of applications where each team decides
to abstract that framework away in subtly?different ways.
However, if I am abstracting out the database, I much prefect?employee.store() over employee-storage.store_record().? Why should I impose "_storage" and "_record" on code that is
abstracting the database details?
?
On Thu, Dec 5, 2019 at 5:47 PM Daniel Olewski <dolewski@...> wrote:
All frameworks change eventually, or stop working when say OS version is updated. Many of us have learned that the hard way … .
Inventing a new RDBMS interface – that means leaking RDBMS world into your application domain. And perhaps also implies the bigger work of covering all of RDBMS functionality. When
we use RDBMS it’s just for storage of data … today. Tomorrow it can be a blob in the cloud, next year it will be something different. I do want my code to be flexible for all those. One way to do that is to isolate on the edge of how the app domain logic uses
the storage. Think `employee_storage.store_employee()` vs `rdbms.insert_record()`. A nuance, but an important one IMO. Once we have that – the app won’t care how the storage is implemented and a simple ‘in-memory employee storage’ or ‘local-file employee storage’
will suffice for unit or some integration testing ..
?
That raises an interesting general question.
"The technology" is a web framework and its linked database framework.
The risk of those changing in a way that breaks a lot of old code is pretty small. How much effort should we spend insulating our code against low-probability events?
Isolating my code from Ecto would in effect mean inventing a *new* interface to a RDBMS. Does it really seem likely that I’ll do that better than the Ecto designers?
P.S. I need to spend some time thinking about how FP ideas and OO ideas play together. My opinion has been that, leaving aside inheritance, typical FP languages are not different,
in practice, than OO languages. But I wonder if that’s right.
?
On Dec 4, 2019, at 12:43 AM, Avi Kessner <akessner@...> wrote:
?
So this is definitely required behavior and it will be your own, so it needs to be tested.
On the other hand, it's a very specific implementation detail which could change when the technology changes and we don't want that to impact actual behavior related tests about
animals.
What I would do is make a transformation decorator and then test sample data to see that it gets transformed correctly.? If Ecto ever changes, that transformation decorator can
do nothing if needed.
You basically need a map of fields that can be changed and the transformation needed for that field.
A bit more work, but it allows for a change in Ecto or a change in understanding of Ecto to not impact the main test suite.
?
On Wed, Dec 4, 2019, 01:50 Brian Marick <marick@...> wrote:
Yes.
?
On Dec 3, 2019, at 5:31 AM, Avi Kessner <akessner@...> wrote:
?
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use
Ecto the need for this abstraction is removed?
?
?
|
Re: "bottom-up" TDD and common behaviors
My team recently had to switch from Hapi to Express (or from Hapi 14 to the new version of Hapi which has some major breaking changes) because Express has a better security update policy for us.
The change was pretty simple because we wrapped all our calls to the framework in an isolated layer.
I wouldn't say we made our own framework, we just made wrappers for the subset of the framework that we directly use.
toggle quoted message
Show quoted text
On Fri, Dec 6, 2019, 02:21 Brian Marick < marick@...> wrote: That raises an interesting general question.
"The technology" is a web framework and its linked database framework.
The risk of those changing in a way that breaks a lot of old code is pretty small. How much effort should we spend insulating our code against low-probability events?
Isolating my code from Ecto would in effect mean inventing a *new* interface to a RDBMS. Does it really seem likely that I’ll do that better than the Ecto designers?
P.S. I need to spend some time thinking about how FP ideas and OO ideas play together. My opinion has been that, leaving aside inheritance, typical FP languages are not different, in practice, than OO languages. But I wonder if that’s right. On Dec 4, 2019, at 12:43 AM, Avi Kessner < akessner@...> wrote:
So this is definitely required behavior and it will be your own, so it needs to be tested. On the other hand, it's a very specific implementation detail which could change when the technology changes and we don't want that to impact actual behavior related tests about animals.
What I would do is make a transformation decorator and then test sample data to see that it gets transformed correctly.? If Ecto ever changes, that transformation decorator can do nothing if needed. You basically need a map of fields that can be changed and the transformation needed for that field.
A bit more work, but it allows for a change in Ecto or a change in understanding of Ecto to not impact the main test suite. On Wed, Dec 4, 2019, 01:50 Brian Marick < marick@...> wrote: Yes. On Dec 3, 2019, at 5:31 AM, Avi Kessner < akessner@...> wrote:
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use Ecto the need for this abstraction is removed?
|
Re: "bottom-up" TDD and common behaviors
Faster and cheaper to program does not mean easier to maintain, especially if the long term owner of the code is maintaining dozens of applications written by different teams with different ideas about what is faster, cheaper, efficient and expressive.? When a framework is chosen, there are reasons, which may indeed include assumptions about the long term maintenance cost of lots of applications.? Ask why in case respecting the choice of framework is more in the customer's long term interest than abstracting the framework into your way of coding.
In XP, we endeavor to write logic in the business domain language instead of the technical language of bits, bytes, programatic structures, data structures, database records, datastores, etc.??
toggle quoted message
Show quoted text
On Thu, Dec 5, 2019 at 7:00 PM Daniel Olewski < dolewski@...> wrote:
In my experience the separation of application logic and externals is much faster and cheaper than most think. Even more including any long-term benefits when things change. It’s just one of the code design activities which we do anyways.
Pays off in weeks – not years … .
?
`store-record()` to me implies storing in a single db record, which leaks ‘db’ term into this surface.
`employee_storage.store(employee)` is more general – don’t care if it’s storing in a record, many records or somewhere else. That’s why I prefer the latter.
?
employee.store() – this seems to imply that the employee needs to know how to store/retrieve itself? That’s a coupling I’d rather avoid – I’d rather couple to a shape of data (Employee) and pass it around freely across classes/processes/languages/operating
system/computers … . Employee would typically already have a single responsibility of keeping itself valid (what’s required/optional, valid values, etc.) – why add another responsibility and more coupling?
?
We should listen to our paying customers who may have some thoughts on the future of their company and their application.? They may not want to pay for the extra time and effort to abstract a framework?that they may have chosen for a specific
business reason,? They?may prefer to be maintaining a family of applications that leverage the same framework in the obvious direct way instead of a family of applications where each team decides to abstract that framework away in subtly?different ways.
However, if I am abstracting out the database, I much prefect?employee.store() over employee-storage.store_record().? Why should I impose "_storage" and "_record" on code that is abstracting the database details?
?
On Thu, Dec 5, 2019 at 5:47 PM Daniel Olewski <dolewski@...> wrote:
All frameworks change eventually, or stop working when say OS version is updated. Many of us have learned that the hard way … .
Inventing a new RDBMS interface – that means leaking RDBMS world into your application domain. And perhaps also implies the bigger work of covering all of RDBMS functionality. When
we use RDBMS it’s just for storage of data … today. Tomorrow it can be a blob in the cloud, next year it will be something different. I do want my code to be flexible for all those. One way to do that is to isolate on the edge of how the app domain logic uses
the storage. Think `employee_storage.store_employee()` vs `rdbms.insert_record()`. A nuance, but an important one IMO. Once we have that – the app won’t care how the storage is implemented and a simple ‘in-memory employee storage’ or ‘local-file employee storage’
will suffice for unit or some integration testing ..
?
That raises an interesting general question.
"The technology" is a web framework and its linked database framework.
The risk of those changing in a way that breaks a lot of old code is pretty small. How much effort should we spend insulating our code against low-probability events?
Isolating my code from Ecto would in effect mean inventing a *new* interface to a RDBMS. Does it really seem likely that I’ll do that better than the Ecto designers?
P.S. I need to spend some time thinking about how FP ideas and OO ideas play together. My opinion has been that, leaving aside inheritance, typical FP languages are not different,
in practice, than OO languages. But I wonder if that’s right.
?
On Dec 4, 2019, at 12:43 AM, Avi Kessner <akessner@...> wrote:
?
So this is definitely required behavior and it will be your own, so it needs to be tested.
On the other hand, it's a very specific implementation detail which could change when the technology changes and we don't want that to impact actual behavior related tests about
animals.
What I would do is make a transformation decorator and then test sample data to see that it gets transformed correctly.? If Ecto ever changes, that transformation decorator can
do nothing if needed.
You basically need a map of fields that can be changed and the transformation needed for that field.
A bit more work, but it allows for a change in Ecto or a change in understanding of Ecto to not impact the main test suite.
?
On Wed, Dec 4, 2019, 01:50 Brian Marick <marick@...> wrote:
Yes.
?
On Dec 3, 2019, at 5:31 AM, Avi Kessner <akessner@...> wrote:
?
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use
Ecto the need for this abstraction is removed?
?
?
|
Re: "bottom-up" TDD and common behaviors
In my experience the separation of application logic and externals is much faster and cheaper than most think. Even more including any long-term benefits when things change. It’s just one of the code design activities which we do anyways.
Pays off in weeks – not years … .
?
`store-record()` to me implies storing in a single db record, which leaks ‘db’ term into this surface.
`employee_storage.store(employee)` is more general – don’t care if it’s storing in a record, many records or somewhere else. That’s why I prefer the latter.
?
employee.store() – this seems to imply that the employee needs to know how to store/retrieve itself? That’s a coupling I’d rather avoid – I’d rather couple to a shape of data (Employee) and pass it around freely across classes/processes/languages/operating
system/computers … . Employee would typically already have a single responsibility of keeping itself valid (what’s required/optional, valid values, etc.) – why add another responsibility and more coupling?
?
We should listen to our paying customers who may have some thoughts on the future of their company and their application.? They may not want to pay for the extra time and effort to abstract a framework?that they may have chosen for a specific
business reason,? They?may prefer to be maintaining a family of applications that leverage the same framework in the obvious direct way instead of a family of applications where each team decides to abstract that framework away in subtly?different ways.
However, if I am abstracting out the database, I much prefect?employee.store() over employee-storage.store_record().? Why should I impose "_storage" and "_record" on code that is abstracting the database details?
?
toggle quoted message
Show quoted text
On Thu, Dec 5, 2019 at 5:47 PM Daniel Olewski < dolewski@...> wrote:
All frameworks change eventually, or stop working when say OS version is updated. Many of us have learned that the hard way … .
Inventing a new RDBMS interface – that means leaking RDBMS world into your application domain. And perhaps also implies the bigger work of covering all of RDBMS functionality. When
we use RDBMS it’s just for storage of data … today. Tomorrow it can be a blob in the cloud, next year it will be something different. I do want my code to be flexible for all those. One way to do that is to isolate on the edge of how the app domain logic uses
the storage. Think `employee_storage.store_employee()` vs `rdbms.insert_record()`. A nuance, but an important one IMO. Once we have that – the app won’t care how the storage is implemented and a simple ‘in-memory employee storage’ or ‘local-file employee storage’
will suffice for unit or some integration testing ..
?
That raises an interesting general question.
"The technology" is a web framework and its linked database framework.
The risk of those changing in a way that breaks a lot of old code is pretty small. How much effort should we spend insulating our code against low-probability events?
Isolating my code from Ecto would in effect mean inventing a *new* interface to a RDBMS. Does it really seem likely that I’ll do that better than the Ecto designers?
P.S. I need to spend some time thinking about how FP ideas and OO ideas play together. My opinion has been that, leaving aside inheritance, typical FP languages are not different,
in practice, than OO languages. But I wonder if that’s right.
?
On Dec 4, 2019, at 12:43 AM, Avi Kessner <akessner@...> wrote:
?
So this is definitely required behavior and it will be your own, so it needs to be tested.
On the other hand, it's a very specific implementation detail which could change when the technology changes and we don't want that to impact actual behavior related tests about
animals.
What I would do is make a transformation decorator and then test sample data to see that it gets transformed correctly.? If Ecto ever changes, that transformation decorator can
do nothing if needed.
You basically need a map of fields that can be changed and the transformation needed for that field.
A bit more work, but it allows for a change in Ecto or a change in understanding of Ecto to not impact the main test suite.
?
On Wed, Dec 4, 2019, 01:50 Brian Marick <marick@...> wrote:
Yes.
?
On Dec 3, 2019, at 5:31 AM, Avi Kessner <akessner@...> wrote:
?
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use
Ecto the need for this abstraction is removed?
?
?
|
Re: "bottom-up" TDD and common behaviors
We should listen to our paying customers who may have some thoughts on the future of their company and their application.? They may not want to pay for the extra time and effort to abstract a framework?that they may have chosen for a specific business reason,? They?may prefer to be maintaining a family of applications that leverage the same framework in the obvious direct way instead of a family of applications where each team decides to abstract that framework away in subtly?different ways.
However, if I am abstracting out the database, I much prefect?employee.store() over employee-storage.store_record().? Why should I impose "_storage" and "_record" on code that is abstracting the database details?
toggle quoted message
Show quoted text
On Thu, Dec 5, 2019 at 5:47 PM Daniel Olewski < dolewski@...> wrote:
All frameworks change eventually, or stop working when say OS version is updated. Many of us have learned that the hard way … .
Inventing a new RDBMS interface – that means leaking RDBMS world into your application domain. And perhaps also implies the bigger work of covering all of RDBMS functionality. When we use RDBMS it’s just for storage of data … today. Tomorrow
it can be a blob in the cloud, next year it will be something different. I do want my code to be flexible for all those. One way to do that is to isolate on the edge of how the app domain logic uses the storage. Think `employee_storage.store_employee()` vs
`rdbms.insert_record()`. A nuance, but an important one IMO. Once we have that – the app won’t care how the storage is implemented and a simple ‘in-memory employee storage’ or ‘local-file employee storage’ will suffice for unit or some integration testing
..
?
That raises an interesting general question.
"The technology" is a web framework and its linked database framework.
The risk of those changing in a way that breaks a lot of old code is pretty small. How much effort should we spend insulating our code against low-probability events?
Isolating my code from Ecto would in effect mean inventing a *new* interface to a RDBMS. Does it really seem likely that I’ll do that better than the Ecto designers?
P.S. I need to spend some time thinking about how FP ideas and OO ideas play together. My opinion has been that, leaving aside inheritance, typical FP languages are not different, in practice, than OO languages. But I wonder if that’s right.
On Dec 4, 2019, at 12:43 AM, Avi Kessner <akessner@...> wrote:
?
So this is definitely required behavior and it will be your own, so it needs to be tested.
On the other hand, it's a very specific implementation detail which could change when the technology changes and we don't want that to impact actual behavior related tests about animals.
What I would do is make a transformation decorator and then test sample data to see that it gets transformed correctly.? If Ecto ever changes, that transformation decorator can do nothing if needed.
You basically need a map of fields that can be changed and the transformation needed for that field.
A bit more work, but it allows for a change in Ecto or a change in understanding of Ecto to not impact the main test suite.
?
On Wed, Dec 4, 2019, 01:50 Brian Marick <marick@...> wrote:
Yes.
On Dec 3, 2019, at 5:31 AM, Avi Kessner <akessner@...> wrote:
?
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use Ecto the need for this abstraction is removed?
?
?
|
Re: "bottom-up" TDD and common behaviors
All frameworks change eventually, or stop working when say OS version is updated. Many of us have learned that the hard way … .
Inventing a new RDBMS interface – that means leaking RDBMS world into your application domain. And perhaps also implies the bigger work of covering all of RDBMS functionality. When we use RDBMS it’s just for storage of data … today. Tomorrow
it can be a blob in the cloud, next year it will be something different. I do want my code to be flexible for all those. One way to do that is to isolate on the edge of how the app domain logic uses the storage. Think `employee_storage.store_employee()` vs
`rdbms.insert_record()`. A nuance, but an important one IMO. Once we have that – the app won’t care how the storage is implemented and a simple ‘in-memory employee storage’ or ‘local-file employee storage’ will suffice for unit or some integration testing
..
?
That raises an interesting general question.
"The technology" is a web framework and its linked database framework.
The risk of those changing in a way that breaks a lot of old code is pretty small. How much effort should we spend insulating our code against low-probability events?
Isolating my code from Ecto would in effect mean inventing a *new* interface to a RDBMS. Does it really seem likely that I’ll do that better than the Ecto designers?
P.S. I need to spend some time thinking about how FP ideas and OO ideas play together. My opinion has been that, leaving aside inheritance, typical FP languages are not different, in practice, than OO languages. But I wonder if that’s right.
toggle quoted message
Show quoted text
On Dec 4, 2019, at 12:43 AM, Avi Kessner <akessner@...> wrote:
?
So this is definitely required behavior and it will be your own, so it needs to be tested.
On the other hand, it's a very specific implementation detail which could change when the technology changes and we don't want that to impact actual behavior related tests about animals.
What I would do is make a transformation decorator and then test sample data to see that it gets transformed correctly.? If Ecto ever changes, that transformation decorator can do nothing if needed.
You basically need a map of fields that can be changed and the transformation needed for that field.
A bit more work, but it allows for a change in Ecto or a change in understanding of Ecto to not impact the main test suite.
?
On Wed, Dec 4, 2019, 01:50 Brian Marick <marick@...> wrote:
Yes.
On Dec 3, 2019, at 5:31 AM, Avi Kessner <akessner@...> wrote:
?
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use Ecto the need for this abstraction is removed?
?
?
|
Re: "bottom-up" TDD and common behaviors
That raises an interesting general question.
"The technology" is a web framework and its linked database framework.
The risk of those changing in a way that breaks a lot of old code is pretty small. How much effort should we spend insulating our code against low-probability events?
Isolating my code from Ecto would in effect mean inventing a *new* interface to a RDBMS. Does it really seem likely that I’ll do that better than the Ecto designers?
P.S. I need to spend some time thinking about how FP ideas and OO ideas play together. My opinion has been that, leaving aside inheritance, typical FP languages are not different, in practice, than OO languages. But I wonder if that’s right.
toggle quoted message
Show quoted text
On Dec 4, 2019, at 12:43 AM, Avi Kessner < akessner@...> wrote:
So this is definitely required behavior and it will be your own, so it needs to be tested. On the other hand, it's a very specific implementation detail which could change when the technology changes and we don't want that to impact actual behavior related tests about animals.
What I would do is make a transformation decorator and then test sample data to see that it gets transformed correctly.? If Ecto ever changes, that transformation decorator can do nothing if needed. You basically need a map of fields that can be changed and the transformation needed for that field.
A bit more work, but it allows for a change in Ecto or a change in understanding of Ecto to not impact the main test suite. On Wed, Dec 4, 2019, 01:50 Brian Marick < marick@...> wrote: Yes. On Dec 3, 2019, at 5:31 AM, Avi Kessner < akessner@...> wrote:
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use Ecto the need for this abstraction is removed?
|
Re: "bottom-up" TDD and common behaviors
I am not seeing that, but I don’t use Gmail.?
Could it be because of my habit of trimming quoted text?
toggle quoted message
Show quoted text
I don't know why, but it seems this thread?is completely separated, at least in my Gmail inbox, from the original Brian's one.Am I the only one seeing?this? :|
|
Re: "bottom-up" TDD and common behaviors
I see it as well. I've noticed other threads being split as well.
toggle quoted message
Show quoted text
I don't know why, but it seems this thread?is completely separated, at least in my Gmail inbox, from the original Brian's one. Am I the only one seeing?this? :|
On Wed, 4 Dec 2019 at 07:44, Avi Kessner < akessner@...> wrote: So this is definitely required behavior and it will be your own, so it needs to be tested. On the other hand, it's a very specific implementation detail which could change when the technology changes and we don't want that to impact actual behavior related tests about animals.
What I would do is make a transformation decorator and then test sample data to see that it gets transformed correctly.? If Ecto ever changes, that transformation decorator can do nothing if needed. You basically need a map of fields that can be changed and the transformation needed for that field.
A bit more work, but it allows for a change in Ecto or a change in understanding of Ecto to not impact the main test suite.
On Wed, Dec 4, 2019, 01:50 Brian Marick < marick@...> wrote: Yes. On Dec 3, 2019, at 5:31 AM, Avi Kessner < akessner@...> wrote:
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use Ecto the need for this abstraction is removed?
--
? | ? |  |
|
Re: "bottom-up" TDD and common behaviors
I don't know why, but it seems this thread?is completely separated, at least in my Gmail inbox, from the original Brian's one. Am I the only one seeing?this? :|
toggle quoted message
Show quoted text
On Wed, 4 Dec 2019 at 07:44, Avi Kessner < akessner@...> wrote: So this is definitely required behavior and it will be your own, so it needs to be tested. On the other hand, it's a very specific implementation detail which could change when the technology changes and we don't want that to impact actual behavior related tests about animals.
What I would do is make a transformation decorator and then test sample data to see that it gets transformed correctly.? If Ecto ever changes, that transformation decorator can do nothing if needed. You basically need a map of fields that can be changed and the transformation needed for that field.
A bit more work, but it allows for a change in Ecto or a change in understanding of Ecto to not impact the main test suite.
On Wed, Dec 4, 2019, 01:50 Brian Marick < marick@...> wrote: Yes. On Dec 3, 2019, at 5:31 AM, Avi Kessner < akessner@...> wrote:
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use Ecto the need for this abstraction is removed?
-- ? | ? |  |
|
Re: "bottom-up" TDD and common behaviors
So this is definitely required behavior and it will be your own, so it needs to be tested. On the other hand, it's a very specific implementation detail which could change when the technology changes and we don't want that to impact actual behavior related tests about animals.
What I would do is make a transformation decorator and then test sample data to see that it gets transformed correctly.? If Ecto ever changes, that transformation decorator can do nothing if needed. You basically need a map of fields that can be changed and the transformation needed for that field.
A bit more work, but it allows for a change in Ecto or a change in understanding of Ecto to not impact the main test suite.
toggle quoted message
Show quoted text
On Wed, Dec 4, 2019, 01:50 Brian Marick < marick@...> wrote: Yes. On Dec 3, 2019, at 5:31 AM, Avi Kessner < akessner@...> wrote:
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use Ecto the need for this abstraction is removed?
|
Re: "bottom-up" TDD and common behaviors
Very helpful, thanks. I'd pair with you but I don't need a cold. :)
R On Dec 3, 2019, at 6:50 PM, Brian Marick < marick@...> wrote:
I hope this makes sense. I have a bad cold, and the old brain is not working well.
Ron Jeffries
I'm really pissed off by what people are passing off as "agile" these days. You may have a red car, but that does not make it a Ferrari. ? -- Steve Hayes
|
Re: "bottom-up" TDD and common behaviors
By the way. I’m happy to pair with people on this codebase. I’m using it to learn, and pairing would help me learn. And I do hope someday to explain what I learn, and pairing would be practice for that.
This is Elixir code, which is not object-oriented. In practice, I don’t think that makes a huge difference. I’ve found the dynamically-typed functional languages similar enough to OO languages. Giving up on mutable data isn’t a big deal. A class isn’t *that* different from a namespace for a single data structure (like `Animal`) that includes a collection of functions. Instead of writing:
animal.operation(5)
… you write:
Animal.operation(animal, 5)
There’s even usually polymorphic function names (so you can drop the `Animal` and let the runtime figure it out), but without inheritance.
|
Re: "bottom-up" TDD and common behaviors
On Dec 3, 2019, at 6:49 AM, Ron Jeffries <ronjeffriesacm@...> wrote: I hope this makes sense. I have a bad cold, and the old brain is not working well. TL;DR: You gave me the idea that the non-mocking solution to the problem would be: 1. The `put_updatable_fields` function sets a field that says “this particular structure is known to be updatable”. 2. We believe the value of that field corresponds to reality because of tests. 3. The tests for the N various functions that return updatable animals just check that field. The disadvantage is that I’d still have to arrange that reading an animal returns an animal that `put_updatable_fields` could handle, rather than a metaconstant. Not sure how I feel about the tradeoffs. ======= It seems to me that this operation, read and make updatable (maybe form-compatible), would properly be a constructor. Yes. I at one point had a variety of structures - they’d be subclasses in a language with inheritance - for different animal use cases. That caused various problems that made me decide I wasn’t going to rebel against what (I think) Ecto wants me to do. I want a test, probably, for put_updatable_fields, and it needs to check all the fields. Since that takes and returns an animal, it can be tested with a canned empty animal and a canned full one and an irritating compare equal of some kind. But in mocks, I think it'd be much the same, since we have to check each field to see if it has been changed.
Yes. I wasn’t explicit, but the two functions called in `updatable!` have to be tested in a non-mockish way. I guess if I were mocking, then I'd just do the mock thing you show that says that updatable! does in fact call the put. Since I wouldn't likely be mocking (Detroit school, y'know), what would I do? I'd write a test that would fail if some should-be-updatable field wasn't updatable. (Which I really think means displayable or something.)
What I’m now calling “updatable” used to be “showable”, but it turns out that what I really mean is “the animal structure contains populated fields that can both be shown in a form and that, when changed, provoke an Ecto `update` to generate the right SQL to update/create the right table rows.” So “updatable” was a better reminder. I'm guessing that a non-updatable animal is still a valid animal. (That might be a bug.) It appears that updatable is a pure cosmetic thing to get the form to work, and that the animal has no reason to know whether it is updatable. I'm wondering why they aren't always made updatable out of the box. (Complete Constructor Method). If they were, would that change our thinking on how to do this? I think it might: we don't worry any more.
In effect, the four functions are Complete Constructors. What’s really being tested is that the four Complete Constructors work correctly with the lower level `Read` code. That code can only fetch on-disk data; there has to be another layer of processing that fills in the fields that don’t live on disk. In a way, part of the issue here is that Ecto encourages you to have partially-constructed objects, anathema in the tradition you and I come from. For example, when you read an `Animal`, you don’t have to read the entire tree of “included” (linked by a foreign key) structures. If you’re reading an animal for a purpose that has nothing to do with ServiceGaps, you can avoid the join and data transfer of that table’s data. I’ve chosen not to worry about that - this is a small app - but you could imagine an app that needed more efficiency. Anyway, now we're writing four functions which, I guess? could conceivably read animals wrongly, not calling updatable! and we want to be sure that they do?
Tell the truth, since this is a display issue (?), I think I'd just look.
Yes, though there are some complications here. (Forms don’t actually display Animals, they display Changesets which can include error messages associated with the last attempt to process a field, and I find myself annoyingly prone to forgetting to add the HTML-generating code to show the error. Here, for example, is the code that annotates the `name` field with a message like `[name] is already taken”: <%= text_input(f, :name) %> <%= error_tag f, :name %> <br/> I forgot to add the `error_tag` for the service gap fields. But what’s needed here is self-discipline and manual testing, not error-prevention code. Probably. If I worry that someone will try to make these four functions more efficient by removing the updatable bit, though, I need tests.
So either A) I'd have only one way to get an animal and it would be updatable, end of story I think, or
B1) write tests for each of those four functions. Do whatever is_updatable checking one does, one or many fields, etc. B2) observe duplication and remove it. (def check_is_updatable(animal))? B2 is the solution I would probably choose, see note at the top of the page.
|
Re: "bottom-up" TDD and common behaviors
toggle quoted message
Show quoted text
On Dec 3, 2019, at 5:31 AM, Avi Kessner < akessner@...> wrote:
If I understand correctly, the code here is specifically?for doing an Ecto transformation, correct? If you decide not to use Ecto the need for this abstraction is removed?
|
Re: "bottom-up" TDD and common behaviors
Hi Brian,
I certainly don't know ... I'll think aloud and in terms I understand and see what happens. I know less than nothing about Elixir. On Dec 2, 2019, at 7:18 PM, Brian Marick < marick@...> wrote:
However, for various reasons, I was trying to avoid the use of mocks in this app. So, if you were doing this in a non-mockish way, what tests would you have? (Remember, there are four functions that do pretty much the same thing.)
I'm pretending that I just sat down to pair with you, all full of total ignorance but great curiosity.
It seems to me that this operation, read and make updatable (maybe form-compatible), would properly be a constructor.?
I guess that updatable! returns an updated animal (or hurls). I guess animal -> is magically the return result of the Read. I guess that functions return the last thing computed and that put_updatable_fields() will return the animal it updates.?
I want a test, probably, for put_updatable_fields, and it needs to check all the fields. Since that takes and returns an animal, it can be tested with a canned empty animal and a canned full one and an irritating compare equal of some kind. But in mocks, I think it'd be much the same, since we have to check each field to see if it has been changed.?
I guess if I were mocking, then I'd just do the mock thing you show that says that updatable! does in fact call the put. Since I wouldn't likely be mocking (Detroit school, y'know), what would I do? I'd write a test that would fail if some should-be-updatable field wasn't updatable. (Which I really think means displayable or something.)
I'm guessing that a non-updatable animal is still a valid animal. (That might be a bug.) It appears that updatable is a pure cosmetic thing to get the form to work, and that the animal has no reason to know whether it is updatable. I'm wondering why they aren't always made updatable out of the box. (Complete Constructor Method). If they were, would that change our thinking on how to do this? I think it might: we don't worry any more.
Anyway, now we're writing four functions which, I guess? could conceivably read animals wrongly, not calling updatable! and we want to be sure that they do?
Tell the truth, since this is a display issue (?), I think I'd just look. If I worry that someone will try to make these four functions more efficient by removing the updatable bit, though, I need tests.
So either A) I'd have only one way to get an animal and it would be updatable, end of story I think, or
B1) write tests for each of those four functions. Do whatever is_updatable checking one does, one or many fields, etc.? B2) observe duplication and remove it. (def check_is_updatable(animal))?
I'm just about certain that I've missed some key thing here. What is it?
Ron Jeffries
Everything that needs to be said has already been said. But since no one was listening, everything must be said again. -- Andre Gide
|