doodloo
Consider the following scenario:
- A
Normalizerbehaviour with anormalize_emailfunction. - A User module, with a
changesetfunction that should normalize theemailparam. - The
User.changesetfunction calls a default implementation defined such asnormalizer().normalize_email()-normalizer()is defined to returnApplication.get_env(:my_app :normalizer, Utils). It means that by default there’s anormalize_emailfunction in theUtilsmodule that does the job. - Note that many other functions in the
Usermodule use thenormalizer().normalize_email()function. - In a
mocks.exfile in thetest/supportfolder, weMox.defmock(NormalizerMock, for: Normalizer)and intest_helper.exswe simplyApplication.put_env(:my_app :normalizer, NormalizerMock).
Now in our user tests, this is all nice and dandy - we can expect on the mock and make sure that the correct function is called by all the User functions such as the User.changeset one.
The intent is really just to test that the User.changeset and other functions delegate the work to the Utils module - so mocking it gives us a way to verify this. We are not interested in what the Utils.normalize_email function does from these tests and the only place where the Utils.normalize_email function is tested is in its own Utils tests. In other words we just check that the NormalizerMock.normalize_email function was called exactly once with a parameter we control, and that the result of the User.changeset function includes this controlled parameter in its return.
However in other tests that target other files and modules, we would like this mock to be absent completely, and have the User.changeset function normally call onto the Utils.normalize_email function.
For example, in tests related to a registration controller (or say more integration tests), we call functions on a Registration module, itself calling the User.changeset function - and in this case we don’t care to have User.changeset call a mock - instead it should call its default implementation.
Of course, doing things pixel-perfect would require us to actually mock the User.changeset function in the Registration tests, but there is too much overhead here.
Is there a way to have mocks applied only to one test? To one test file? Where would all the moving pieces go (Eg where would the defmock call be, the Application.put_env call etc)?
EDIT: Basically a solution we found is to include stub(NormalizerMock, Utils) in every single other test - which is definitely not acceptable. What we’re trying to understand is how we could do the opposite - have Utils be the default implementation during all tests, except for a few in which we want to mock and expect that it’s being called. Does this make sense? Would it for example be OK to have a macro run before each test suite and bake a setup block for each test calling stub_with NormalizerMock, Utils so tests that don’t use the mock don’t have it, while tests that use the mock could still perform NormalizerMock.expect tests?
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #ai
- #elixirconf-us
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 4- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
doodloo
Replying to myself - something we found would work is the following.
Create a
Casebase module that all test modules willuseinstead ofExUnit.Case:If you also have a
DataCaseor other helpers, don’t forget to have themusethis newTestCasemodule too!Then in all our test modules, we simply
use MyApp.Case, so it sets default implementations instead of mocks. For example:escobera
@doodloo I loved the approach, but I’m having trouble on my project to set it up.
I’ve defined my base
TestCaseas such:But when I try to
useit, instead ofExUnit.CaseTemplateon data_case I getCan you share a little more of how are you configuring your tests?
brettbeatty
That warning would suggest your app isn’t starting up
:ex_unit.If you get that error when running tests, do you have an
ExUnit.start() # possibly with argsin yourtest/test_helper.exs?escobera
This is the contents of my
test/test_helper.exsUpdate:
In order for it to work on my side I had to change all of
test/suport/*_case.exfiles to haveInside its
setupblock, for instance, insidetest/support/data_case.exThis seems pretty different from what @doodloo showed, but I couldn’t make it compile the other way. Do you have any idea what I’m doing wrong here?