Fork me on GitHub
#untangled
<
2016-04-07
>
currentoor04:04:50

This untangled-spec library is pretty awesome.

currentoor04:04:59

I’m just having one issue with it.

currentoor04:04:28

db in line 18 appears to be nil, but this only happens when I use the :seed-fn.

adambros04:04:30

mm i’ve seen that, and usually i put a try catch around the seed fn and print it and i can see the actual error

adambros04:04:45

i might actually investigate why it gets swallowed as i’ve seen it multiple times myself now

currentoor04:04:18

@adambros: in the body of the seed-fn?

adambros04:04:36

usually its an error with your seed data

currentoor04:04:18

wait but how would that work? the seed function just returns some data?

currentoor04:04:08

hmm it looks like the seed function takes in one argument

currentoor04:04:20

I figured maybe I’m doing something obviously stupid 😄

adambros04:04:08

well so i usually see seed-fn as:

adambros04:04:37

key part being link-load-and-seed-data

adambros04:04:56

just because you returned something in your seed function, doesnt mean it actually got installed into your database

adambros04:04:01

thats why you are passed a conn

currentoor04:04:23

so i suppose i was doing something obviously stupid lol

currentoor04:04:31

your function works like a charm!

adambros04:04:34

not stupid, just ignorant

adambros04:04:41

wait that still sounds bad

currentoor04:04:36

I suppose the example in the tutorial is wrong then, I guess that’s why that part is not officially released

adambros04:04:15

yeah, and im not really sure why we even have to install the data ourselves when in most cases it always uses link-and-load-seed-data

adambros04:04:24

your idea makes more sense

adambros04:04:30

if there arent other things you might want to do

currentoor04:04:24

i can try adding adding a way to do what i was trying to do

currentoor04:04:43

as a PR, but maybe you guys want to discuss it first?

adambros04:04:24

feel free to do a PR, but i’ve been itching to make seed-data more declarative than just a sequence of maps

currentoor04:04:35

what did you have in mind?

currentoor04:04:08

i was thinking seed-fn is the function that takes in a connection, and seed takes in just a transaction vector

adambros04:04:29

maybe seed-data is just data or a function of no args that makes data?

adambros04:04:43

seed seems vague, could mean random seed

adambros04:04:53

so :app/whatever’s value gets passed to gen-application

adambros04:04:44

where gen-application takes some data and returns a map suitable for link-and-load-seed-data

adambros04:04:53

which is really just datomic transaction maps

currentoor04:04:57

good idea on seed-data

currentoor04:04:21

not sure how/where gen-seed-data gets used

adambros04:04:30

well so that was an early draft

adambros04:04:52

im thinking the seed-data you are thinking of writing would get passed the part that starts from {:app/twitter ...

adambros04:04:10

and you define somewhere globally what namespaces mean

adambros04:04:16

but this is literally hot off the brain press

adambros04:04:28

and i havent thought/talked it through much

adambros04:04:09

main reason for this is that in the app im working on we have a file with like… nearly 1400 lines of seed data

adambros04:04:29

\>50% could be simplified with gen functions

currentoor04:04:42

oh yeah, i’ve been in a situation like that

currentoor04:04:04

gen-functions are the only sane way to go about it IMO

adambros04:04:04

and maybe made easier to reason about if keyword namespace tell you what type of thing you are referring to PAGES away from where it was made

adambros04:04:20

but anyway glad you got it working

currentoor04:04:55

so any plans to integrate something like (foo) => AnyNumber?

currentoor04:04:14

like a type based assertion result?

adambros04:04:55

in what context?

adambros04:04:11

like as cheap static type checking?

adambros04:04:54

or do mean in seed-data / gen functions?

tony.kay05:04:42

@adambros: @currentoor The connction part of seeding: I think we did that so you could be fully general in how you seed…for example it might be a pre-existing database and you want to run some transform on it…I think that was the reasoning.

tony.kay05:04:21

For tests, I think we made that pure transactions, since you can safely assume an empty database

tony.kay05:04:55

I cannot remember…do you need a Datomic connection to do certain db setup, like install functions?

tony.kay05:04:03

that might be part of it too

tony.kay05:04:24

Just because most of the examples are link-and-load, don’t assume there aren’t broader cases to solve.

tony.kay05:04:32

so yeah, in my example code you’re copying, I think I just screwed up and mis-remembered.

tony.kay05:04:52

I was thinking of protocol-support, where you just supply the data

currentoor05:04:05

@tony.kay: makes sense, so do you think :seed-data option for only txn-data (no function) is not worth it?

currentoor06:04:41

@adambros: yeah basically cheap type checking, i’ve seen that sort of thing in ruby testing libraries. Useful for mocks and checking the result of things.

currentoor06:04:06

prismatic schema would probably be a good thing to use if you wanted to do that

adambros07:04:13

Personally i would like to explore actual static typing checking in a lisp, but prismatic schema seems like an interesting compromise. I might use it as a consumer of untangled when writing seed data generating function.

adambros07:04:27

https://github.com/LuxLang/lux is an interesting attempt at a statically typed lisp on the jvm & js tbh im not sure how likely it succeed or stay in development, but the ideas the creator puts forward are endlessly fascinating to me as someone who enjoys programming language design

tony.kay13:04:12

@currentoor: I’d have to review what we have. I think you want it to be a function for easier composition (tempids as arguments and such)…so, yeah, I’m not seeing a good case for adding yet another option when the current one works well. In general I think thin APIs are just simpler and clearer. I need good common use-case scenarios that are compelling to want to add APIs. Same thing goes for testing “sugar”. It is easy enough with untangled-specs =fn=> arrow to write reusable general-purpose matchers. We will likely add some, but will also likely leave it to the user to build their own style of checkers. From my standpoint, you are testing behaviors. "Returning a number” does not meet the criteria for a good behavior in my book, so no checker. Behavioral testing is about clarity. Type systems are about plumbing. IMHO (partial) integration tests serve as your type system in languages like Clojure. I need to get a talk up on testing…we don’t spend enough time training engineers to do it well. In my experience, about 1 in 50 software developers I meet knows how to test in a sustainable way.

tony.kay13:04:07

That is not a “they are too stupid to do it”…I’ve never met one that couldn’t be doing it in a couple of weeks with some guidance..this is like the invention of the wheel…it’s obvious once you see it, but until then: square wheels everywhere

tony.kay13:04:25

(end rant) simple_smile

currentoor17:04:08

@tony.kay: yeah I'm pretty sure I'm one of the 49 developers. Whenever I wrote behavior tests it always felt wrong, like I was using a hammer as a chisel. Also you make a good point about thin APIs.

currentoor17:04:39

Any suggested resources for learning behavior driven testing?

tony.kay17:04:56

Actually yes, but they are all OO.

tony.kay17:04:04

easy enough to map over the concepts, though

tony.kay17:04:36

I'm going to make some more videos...but I have one on YouTube (I think)....let me see if it is public. There was also a pretty good one at a Seattle meet up a few years back...let me look.

tony.kay17:04:16

See if you can access this:

tony.kay17:04:32

It is an unlisted channel (internal tech talks here), but I think they are unprotected

tony.kay17:04:49

I was presenting to legacy staff that is mostly OO, so it is targeted that way

tony.kay17:04:28

Scott Bain has some good videos that help...this one for example: https://vimeo.com/3356282

currentoor17:04:01

Yeah I can access those. Thanks.

tony.kay17:04:07

The rules are deceptively simple, so I can list them here: 1. Move all side effects to the border (inject side-effect code). This gets you control in your tests and decouples things. Design is important. You can write large swaths of code without ever running a real application. 2. Control everything not directly under test (to a point...no need to control map for example 😉 ). This provides specificity (no cascading failures) 3. Test behaviors, not implementation. This gives you clear reasoning. 4. If a test causes pain (to make/maintain): it is telling you your design is weak (or you need to write tools) Common code smells: Tests longer than a few lines, starting up the world, cascading failures, or anything that is not immediately apparent to a new observer (e.g. the behavior name should be clear, and the code should be just about as clear).

tony.kay17:04:49

a test called "test that f works" is total crap

tony.kay17:04:19

lots of other things I can say (like no negative wording in behaviors), but that gets you started.

currentoor17:04:21

lol "test that f works" is basically the only kind of test i write

tony.kay17:04:43

yep...that is my general experience with the state of the art in our training simple_smile

currentoor17:04:53

well, awesome this seems like a good place to start, thanks again

tony.kay17:04:03

which is why so many ppl give up on it, or get discouraged by failures.

tony.kay17:04:17

Goes back to R. Hickey's talk on practice vs performance

tony.kay17:04:33

we aren't practicing this stuff to learn it, we're just trying to perform, and we're making lots of sour notes

tony.kay17:04:07

you're welcome, of course

adambros17:04:31

it was surprising to me that a Computer Science degree didn’t teach me testing, until i realized its about the science of computing not the engineering which unfortunately is likely why swaths of people dont ever think about testing

adambros17:04:24

and aside from the plumbing, i feel way more comfortable (and effective) about my code with behavioral tests than i do with a static type system

tony.kay17:04:54

I'm adding those videos to my youtube public channel...

davewo18:04:11

How about a behavior like “identifies a unique foo”, where we assert that a) a foo is returned when it is unique, and b) nothing is returned when the foo is not unique?

tony.kay18:04:44

(a) and (b) are your behaviors

tony.kay18:04:13

the combo could be ok if it is rock-solid-clear to any reasonable reader

davewo18:04:13

heh, I always get a funny feeling when behaviors say “returns” in them

tony.kay18:04:23

yeah, "returns" is a code smell in tests

tony.kay18:04:28

so, I could be influenced either way. The foo part of it leans me more towards (a) (b), but saying it more clearly with better names could swing me the other way

tony.kay18:04:12

nothing wrong with "returns" if it is a pure function: (+ 1 2 3) => "+ returns the sum of arguments"

tony.kay18:04:42

that is what it does....level of abstraction

tony.kay18:04:02

"scheduler returns a job" is no good

tony.kay18:04:16

"scheduler returns the next job in priority order that is runnable" is fine