Fork me on GitHub
#component
<
2018-12-07
>
jimbob20:12:33

I am trying to test a subsystem in some smoke tests but cant find a clear way to get the substem from the entire system map? I for example build the system map and start the system usually in production, but in test i want to take this system map and only start one component or some nested components but all the dependents are nil. any way around this?

hiredman21:12:17

a system is a map

hiredman21:12:30

use select-keys to select the subparts you want

seancorfield21:12:46

Or if you're trying to start just that component, remember to specify using for its dependencies (and build those too).

jimbob21:12:55

im trying to start a component and all its dependents

jimbob21:12:44

so say i have a large map. ideally i would be able to do: (component/start-system (:component-with-dependents (large-map)))

jimbob21:12:50

and that would work

seancorfield21:12:01

If you only want the subcomponent, only build that. With its dependencies.

seancorfield21:12:09

Don't try to use the whole system.

jimbob21:12:36

can i not reuse my whole system-map (not started yet) and then refine from there?

seancorfield21:12:45

That's kind of the point of Component -- you can build only the piece you care about for your tests, and provide whatever dependencies you want. So you can mock things out for subcomponent testing.

jimbob21:12:00

right, im not building out or wanting to build oout anything else

jimbob21:12:09

but i do want to reuse the whole system map that is already defined

jimbob21:12:14

and just start subsections

seancorfield21:12:27

That's not how it works and not how it's supposed to work.

seancorfield21:12:41

If you tests are about a subcomponent, they shouldn't even reference the full system.

seancorfield21:12:08

Component is intended to let you provide mock dependencies. That's part of its power for testing.

jimbob21:12:13

probably the best plan then is to build a separate test-system-map for each part im testing

seancorfield21:12:43

For example, at World Singles Networks, we have an "Application" Component with dependencies on Environment, Datasource, Caches components. Datasource and Caches depend on Environment. For the tests on Environment-related stuff, we just build Environment, nothing else. For the tests on Caches, we build the Caches component with an Environment component.

jimbob21:12:07

yes that seems like good discrete separated unit tests

jimbob21:12:09

im smoke testing

jimbob21:12:11

in production

jimbob21:12:33

as part of a deploy step

seancorfield21:12:07

Well then you probably want to build and start the whole application component and run tests on that?

seancorfield21:12:34

Once it is started, you can reach in and pull components out to work with (and they will all be started and have their dependencies).

jimbob21:12:36

not necessarily. not wanting to take traffic just yet for example

jimbob21:12:51

from queues or an api

seancorfield21:12:36

Then your smoke tests will have to build the parts they're interested in, with their dependencies.

jimbob21:12:59

yes. thats kind of where im at with my question hehe..

jimbob21:12:05

subsystems of a production system map

jimbob21:12:10

subcomponents rather

seancorfield21:12:49

If you're not testing the complete system map, ignore it. Build the components you are interested in, with their dependents, and start just those parts.

jimbob21:12:15

yes. For example i have this:

(def test-dpp-client-system-map
  (component/system-map
    :cache-client (cache-client/make-component (:cache-client helpers/config))
    :dpp-client (component/using (cpp/make-component (:dpp-client helpers/config))
                                 [:cache-client])))
instead id like to:
def test-dpp-client-system-map
  (some-how-start-dependents-with-this-top-level-component-in-a-cleaner-way (:dpp-client production-system-map))

jimbob21:12:31

but this is fine and explicit

jimbob21:12:04

i was just wondering if there were more concise albeit implicit ways of also starting the dependents of the thing you are starting

jimbob21:12:13

ok thank you!

seancorfield21:12:11

Your test-dpp-client-system-map would need to create and hold the :cache-client component since :dpp-client depends on it. You can't get away from that.

seancorfield21:12:47

Otherwise, Component has nothing to inject (& start) for that dependency.

jimbob21:12:59

right, i was just wondering if there were out of the box ways, say using the dependency-graph for component to intelligently infer that i want that started first

jimbob21:12:11

since thats kind of how start-system works

jimbob21:12:23

i guess its explicit as well

jimbob21:12:27

but we have the system map

jimbob21:12:37

which dependencies can be inferred.

seancorfield21:12:39

But from :dpp-client's value, that other data is not visible/accessible -- it's not part of that subcomponent.

jimbob21:12:41

i see, because we still need to pass in certain args

seancorfield21:12:52

It's only when you start things that the actual component values get injected -- which means that whatever is started has to have access to all those components. Until that point, the metadata is just keywords.

👍 1
jimbob21:12:54

even though we are standardized here and can probably make a macro or something to do just that

jimbob21:12:48

ok this is beginning to make a lot more sense

seancorfield21:12:02

(it would probably have helped you if component/start threw an exception if a dependency could not be found/injected rather than just leaving things as nil)

jimbob21:12:36

ah thats a good idea

hiredman21:12:57

if I recall, it does throw an exception

seancorfield21:12:12

You are correct Missing dependency :b of clojure.lang.PersistentArrayMap expected in system at :b

hiredman21:12:41

annoyingly it does lose dependency information with select keys, but if you manually dissoc the components you don't want it works

hiredman21:12:03

user=> 
(into {}
      (component/start
       (dissoc
        (component/system-using
         (component/system-map
          :a {}
          :b {}
          :c {})
         {:a [:b]
          :b []})
        :c)))
{:a {:b {}}, :b {}}
user=>