apex-enterprise-patterns / fflib-apex-mocks

An Apex mocking framework for true unit testing in Salesforce, with Stub API support
BSD 3-Clause "New" or "Revised" License
423 stars 214 forks source link

How to .verify that a Domain layer constructor initialized with expected SobjectList #55

Closed cropredyHelix closed 6 years ago

cropredyHelix commented 7 years ago

Use case:

Class A needs to construct a Domain for Opportunity and then invoke a method on that domain object

Opportunities domain = Opportunities.newInstance(someOppoList);
domain.doWork();

If you are unit testing class A and want to use ApexMocks to see if your code under test is passing the right list of opportunities to the domain object, you try in your testmethod :

...
Opportunity] mockOppos = new List<Opportunity> { ...}
((Opportunities)mocks.verify(mockOppoDomain,mocks.times(1).description('domain sb constructed w/ all Oppo in scope')))
                .newInstance(mockOppos);

But this won't compile because Opportunities.newInstance() is a static method and there is no StubAPI support for statics.

Since the pattern says to use Opportunities domain = Opportunities.newInstance(someOppoList); that in turn resolves to:

public static IOpportunities newInstance(List<Opportunity> sObjectList){
        return (IOpportunities) Application.Domain.newInstance(sObjectList);
  }

...I'm a bit puzzled how to use ApexMocks to see if my mockDomain object was constructed with the expected list of sobjects.

N.B. relevant Mockito Stackoverflow seems to suggest this isn't going to be possible.

Is there an alternative besides moving the domain method to a Service layer?

XoNoXForce commented 7 years ago

I think that the best thing to do, it's to move the logic in the service layer. The Domain layer duties in my mind are strictly related to the trigger. That is tested through integration tests. I'm used putting the logic that would change the object in the service layer, and then you can mock it. I never had the need to mock the domain.

cropredyHelix commented 7 years ago

Ah yes, but what about the Example in the Force.com Enterprise Architecture book where the domain layer implemented an isCompliant method? That wasn’t a trigger use casse.


Eric Kintzer

On Oct 6, 2017, at 04:49, Enzo Denti notifications@github.com wrote:

I think that the best thing to do, it's to move the logic in the service layer. The Domain layer duties in my mind are strictly related to the trigger. That is tested through integration tests. I'm used putting the logic that would change the object in the service layer, and then you can mock it. I never had the need to mock the domain.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

dfruddffdc commented 6 years ago

Ah constructor mocking/spying. Unfortunately, I don't think this is possible in ApexMocks. I've been using sinon in nodeJS and had similar issues.

The best I can suggest is having a domain factory method, where the tests create a real Domain instance and verify the records. And then route all of the Domain creation throughout your app through this factory, mocking it out in unit tests.

Closing as wontfix, I think it's just a limitation we have to accept.

afawcett commented 6 years ago

@cropredyHelix yes, if its behavior of the object it can be included in the domain class, it's not a hard and fast rule though, at least if it's in or below the service layer we have logic in a safe place! 👍