It seems that the community is going towards the concept “phoenix is not your application”, creating different apps with clear separation, no more models in the web app, etc. In the current project I’m working on, I find myself testing the same feature twice, for the Context function and the controller response.
To ask a concrete example, would you test
Accounts.register as a unit, as a feature (from the controller), or both? Because they live in different apps, I feel bad testing in one place only, but often the tests look almost duplicated. I’d love to hear your thoughts.
Your tests will hit similar code paths, but they should test different things. E.g. in the context tests you’ll test that e.g. persistance does work and all fields will be saved correctly, whereas the controller test would test that after create actions the redirects/status codes are correct or that the correct data will be rendered to the webpage.
E.g. consider you have fields in a schema, which are only relevant to your business logic, but should not be disclosed at the web layer. The context tests would test for the existence and the ability to edit the field, while the controller test would rather check for the absence of it in the views and that the field is not editable.
That makes sense. In one test we’re looking for
nil and in the other we’re looking for
404 not found.
The setup for both context and controller tests (the records that need to exist in order for the test to run) still bothers me that I have to duplicate. In Rails I mostly write feature tests, and I would do the same in Phoenix if there wasn’t multiple apps.
Maybe I’m looking too much into this…
To some degree I think this is a transition period moving from the “old” way to the new way using contexts.
I noticed myself when migrating an old python app to phoenix by just translating the old models and controllers that there seemed to be repetition in testing between the web layer and the context layer.
The reason I think is that the design is in fact the same and hence the feeling of an artificial layer which doesn’t seem to buy you much.
However, when I have started applications from scratch using the knowledge of contexts (or rather separation of web layer and application) my design end up a bit different and the web and context layer test different things and compose in different ways.
This may be the same thing as when moving from programming OO to programming in a functional language. Initially the code doesn’t look too good because one tries to fit a square peg into a round hole. But once one get more familiar with the functional paradigm the code changes as well.
I’m also struggling along this line of separating controller and context tests without repeating myself. Does anyone know of any open source projects using Phoenix 1.3 in the wild that do a decent job of this (or at least try to)? The official docs for testing aren’t quite there yet.
Having a good working factory (global; not per test file) was the biggest step forward for me in that regard. Because if your tests are like “arrange / act / assert”, then in my mind only the “arrange” part should be similar between context and controller tests. With a factory, this should get kinda DRY besides actually using the factory functions.
The “act” phase should by definition be different like calling context functions vs. connecting to an http endpoint. Similarly in the “assert” phase the context tests use the returned values and maybe other context functions to validate the correctness, whereas the controller tests assert on the returned conn struct or check some other http endpoints.
I don’t think it’s that much a transition from old to context, but rather a side-effect of CRUD. If all your app does is being a window to call crud based context functions the perceived duplication will be a lot. The more an application does deviate from that the more the tests will become different.
E.g. only creating an entity will look super similar from a controller test to a context test. But as soon as the controller does not only create the entity, but is also responsible to send a notification email and write to some kind of log it’s becoming more interesting. For such a setup there would probably be 3 different context tests for “create entity”, “send notification” and “log action”, whereas the controller test would act once, but assert that all three things do happen.