Code Monkey home page Code Monkey logo

Comments (16)

dfruddffdc avatar dfruddffdc commented on May 28, 2024

Great writeup, thanks Eric!

For your specific case, you could use the sobjectWithId matcher. But I understand your general sentiment. It's a legitimate concern, but still kind of funny 🤣.

Recreating the exception is trivial.

Account a = new Account();
a.get(Case.Id);

Or the similar, SObject row was retrieved via SOQL without querying the requested field exception, even if the field belongs to the object.

for (Account a: [SELECT Id FROM Account]) {
	a.get(Account.Name);
}

Like you say, it's WAD. The matchers naively try to retrieve all the field values from all the SObject records, silently swallowing up the inevitable spurious exceptions.

So what to do..

As a workaround, perhaps use a captor or a custom matcher instead?

But in ApexMocks itself.... You say we can't be more selective with the object type corresponding to the SObjectFields. But perhaps we can. We just need to supply the map as something that does have an associated SObjectType.... perhaps an SObject? :)

public class SObjectWith implements fflib_IMatcher
{
	private SObject toMatch;

	public SObjectWith(SObject toMatch)
	{
		this.toMatch = validate(toMatch);
	}

	public Boolean matches(Object arg)
	{
		if (arg != null && arg instanceof SObject)
		{
			SObject soArg = (SObject)arg;
			if (soArg.getSObjectType() != this.toMatch.getSObjectType())
			{
				return false;
			}
			if (!sobjectMatches(soArg, this.toMatch.getPopulatedFieldsAsMap()))
			{
				return false;
			}
			return true;
		}

		return false;
	}
}

If you want to submit a PR along those lines, I'll be happy to review (also to include a version for a List). Otherwise, I'll try and get this in at some point.

from fflib-apex-mocks.

cropredyHelix avatar cropredyHelix commented on May 28, 2024

This is pretty clever; of course the old constructor that supports a map argument will need to continue to be supported.

from fflib-apex-mocks.

bradedge avatar bradedge commented on May 28, 2024

Hi team, any progress on this item? We are using mocks for integration tests. That is, the unit of work contains different object types. However, it appears we can not use mocks because of this limitation. Thank you!

from fflib-apex-mocks.

ImJohnMDaniel avatar ImJohnMDaniel commented on May 28, 2024

@bradedge -- Can you post a working example somewhere for us to review and investigate with?

from fflib-apex-mocks.

bradedge avatar bradedge commented on May 28, 2024

In our main class we have:

uow.registerDirty(transObj);
uow.registerDirty(invItemObj);

In our test class, this verify works:

((IApplicationSObjectUnitOfWork)
mocks.verify(uowMock, mocks.times(3).description('Expected 3 transaction updated.'))).registerDirty(
    fflib_Match.sObjectWith(
    new Map<SObjectField, Object>{
        Transaction__c.Order__c => orderList[0].Id
} ));

This next verify throws an error:

((IApplicationSObjectUnitOfWork) 
mocks.verify(uowMock, mocks.times(1).description('Expected 1 invoice item updated.'))).registerDirty(
    fflib_Match.sObjectWith(
    new Map<SObjectField, Object>{
        Invoice_Item__c.Order__c => orderList[0].Id,
} )); 

Error message:
fflib_ApexMocks.ApexMocksException: Expected : 1, Actual: 0 -- Wanted but not invoked IApplicationSObjectUnitOfWork__sfdc_ApexStub.registerDirty(SObject)

The log file shows:
System.SObjectException: Invoice_Item__c.Order__c does not belong to SObject type Transaction__c

from fflib-apex-mocks.

stohn777 avatar stohn777 commented on May 28, 2024

@bradedge
Is a stacktrace available for the error as well, indicating where in the FFLIB code the error is occurring? Thanks.

from fflib-apex-mocks.

cropredyHelix avatar cropredyHelix commented on May 28, 2024

@bradedge The exception is normal in the debug log, the ApexMocks eats this error (you can see in the code). Have you looked at Why Aren't My ApexMocks working?.

I would say that invItemObj does not have field Order__c set to orderList[0].Id or, by the time the mocks.verify is executed, you've cleared/changed the value of Order__c

from fflib-apex-mocks.

bradedge avatar bradedge commented on May 28, 2024

@cropredyHelix, thank you. We have been through "Why Aren't My ApexMocks working?".

When we read the issue you originally posted above, it describes the behavior we are getting and leads me to believe that mocks will not allow us to verify when we have multiple object types in the UOW (for example, in integration tests that perform actions on many object types).

Am incorrect? Does mocks allow us to verify multiple object types and you suspect we have an error elsewhere?

from fflib-apex-mocks.

bradedge avatar bradedge commented on May 28, 2024

I would appreciate some syntax help in a verify statement, to get through this item...

We have successfully verified objects in the unit of work that were added using fflib_SObjectUnitOfWork.registerNew(SObject record) using this statement:

    ((IApplicationSObjectUnitOfWork)
    mocks.verify(uowMock, mocks.times(2).description(
        'Expected 2 invoices.'))).registerNew(
          fflib_Match.sObjectWith(
            new Map<SObjectField, Object>{
              Invoice__c.Order__c => orderList[0].Id
            }
          )
        );

However, this syntax does not work for records that were created using the overloaded "registerNew(SObject record, Schema.sObjectField relatedToParentField, SObject relatedToParentRecord)".

How do I match against items in the overloaded registerNew method? Thank you

from fflib-apex-mocks.

stohn777 avatar stohn777 commented on May 28, 2024

@bradedge,
In an earlier post, I commented that a stacktrace would be helpful in pinpointing the issue. Without, a probable cause was located, and a stracktrace could verify. Thanks.

from fflib-apex-mocks.

bradedge avatar bradedge commented on May 28, 2024

Failing on:

((IApplicationSObjectUnitOfWork)
   mocks.verify(uowMock, mocks.times(2).description(
       'Expected 2 invoice items created for transList[0] and transList[2] (which goes on a new invoice and invoice item).'))).registerNew(
         fflib_Match.sObjectWith(
           new Map<SObjectField, Object>{
             Invoice_Item__c.Unit_Price__c => 5
           }
         )
       );

Stack trace:

Class.fflib_MethodVerifier.throwException: line 81, column 1
Class.fflib_AnyOrder.verify: line 45, column 1
Class.fflib_MethodVerifier.verifyMethodCall: line 20, column 1
Class.fflib_ApexMocks.verifyMethodCall: line 139, column 1
Class.fflib_ApexMocks: line 266, column 1
Class.fflib_ApexMocks: line 87, column 1
Class.IApplicationSObjectUnitOfWork__sfdc_ApexStub.registerNew: line 241, column 1
Class.InvoiceTransactionMockTest.singleOrderAndTranscationScenario1: line 145, column 1
12:40:55.0 (662859775)|FATAL_ERROR|fflib_ApexMocks.ApexMocksException: Expected : 2, Actual: 0 -- Wanted but not invoked: IApplicationSObjectUnitOfWork__sfdc_ApexStub.registerNew(SObject). Expected 2 invoice items created for transList[0] and transList[2] (which goes on a new invoice and invoice item).

from fflib-apex-mocks.

stohn777 avatar stohn777 commented on May 28, 2024

@bradedge,
That stacktrace appears related to the Assertion failure. You mentioned an exception detailed in the log for "System.SObjectException: Invoice_Item__c.Order__c does not belong to SObject type Transaction__c". Does a stacktrace exist for that? Thanks again.

from fflib-apex-mocks.

cropredyHelix avatar cropredyHelix commented on May 28, 2024

@bradedge

  1. You can definitely use UnitOfWork on multiple SobjectTypes and verify results using ApexMocks. Our org's code does this in numerous places
  2. If you want to mock against registerNew(SObject record, Schema.sObjectField relatedToParentField, SObject relatedToParentRecord) ...
((IApplicationSObjectUnitOfWork)
   mocks.verify(uowMock, mocks.times(2).description(
       'Expected  invoice items created for transList[0] and [2] each with Unit Price of 5.00')))
      .registerNew(
         fflib_Match.sObjectWith(
           new Map<SObjectField, Object>{
             Invoice_Item__c.Unit_Price__c => 5
           }
         ),
         fflib_Match.eqSObjectField(Invoice_Item__c.Transaction__c),
	fflib_Match.sObjectOfType(Transaction__c.SobjectType)
       );

Now, if you are verifying that only Trans[0] and Trans[2] are linked, you'll need two different mocks.verify

For the first Invoice Item

((IApplicationSObjectUnitOfWork)
   mocks.verify(uowMock, mocks.times(1).description(
       'Expected  invoice items created for transList[0]  with Unit Price of 5.00')))
      .registerNew(
         fflib_Match.sObjectWith(
           new Map<SObjectField, Object>{
             Invoice_Item__c.Unit_Price__c => 5
           }
         ),
         fflib_Match.eqSObjectField(Invoice_Item__c.Transaction__c),
	 fflib_Match.sObjectWith(
          new Map<SObjectField, Object>{
             Transaction__c.Id => transList[0].Id
           }
        )
       );

For the second item

((IApplicationSObjectUnitOfWork)
   mocks.verify(uowMock, mocks.times(1).description(
       'Expected  invoice items created for transList[2]  with Unit Price of 5.00')))
      .registerNew(
         fflib_Match.sObjectWith(
           new Map<SObjectField, Object>{
             Invoice_Item__c.Unit_Price__c => 5
           }
         ),
         fflib_Match.eqSObjectField(Invoice_Item__c.Transaction__c),
	 fflib_Match.sObjectWith(
          new Map<SObjectField, Object>{
             Transaction__c.Id => transList[2].Id
           }
        )
       );

Now, perhaps I'm not understanding your use case but what you are trying to do is eminently doable. Each mocks.verify uses matchers against the method signature. If you use matchers for one arg, you have to use matchers for all args.

from fflib-apex-mocks.

bradedge avatar bradedge commented on May 28, 2024

@cropredyHelix that resolved our issue. Thank you all for the support and help through this! Also, thank you for supporting the community on this killer framework.

from fflib-apex-mocks.

daveespo avatar daveespo commented on May 28, 2024

Thanks all for pitching in here

from fflib-apex-mocks.

cropredyHelix avatar cropredyHelix commented on May 28, 2024

@daveespo - not sure why this was closed as while we solved @bradedge issue, the underlying issue in OP was flagged as an enhancement by @dfruddffdc 2018-02-21. Was the enhancement ever implemented? (perhaps in some other Issue)

from fflib-apex-mocks.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.