Closed donnie745 closed 8 years ago
Hi Donnie,
The ApexMocks Generator has no real knowledge of Apex syntax. It parses the plain text in the static code files. Parsing text is inherently risky, so we expect the files to be in a certain format and don't attempt to traverse the interface inheritance hierarchy.
Not only would it be generally difficult to find the parent interface definition, but the parent interface might not even be in the static code - if you are extending a native interface such as System.Schedulable, or a global interface defined in another managed package.
There are ways around this however, as demonstrated in the apex-common sample app.
To put it in the context of your examples above, you should generate mocks classes for both interfaces.
interfacemocks.properties:
IBaseInterface=BaseInterface
ISuperInterface=SuperInterface:BaseInterface
This will generate 2 classes. The Mocks.SuperInterface extends the BaseInterface, and therefore defines a public void MyBaseMethod()
method and can be mocked as normal.
I'm going to mark this case as closed, but obviously get back in touch if anything I've written here doesn't make sense!
Excellent, thanks for that. In my second week of Salesforce development so hadn't appreciated the complexities you mentioned above. This will resolve my issue for the particular test mock classes I was wanting to generate.
I guess there is a bit of complication if we wish to mock a class that implements more than 1 interface (e.g. a class implementing mix-in interfaces) then.
For example, given we have a class MySuperClass that implements mixin interfaces IMyBaseIntefaceA, IMyBaseInterfaceB and IMyBaseInterfaceC, would I be right in thinking the only workaround for this would be as below?: i) Create an interface IMyBaseInterfaceAB that extends IMyBaseInterfaceA and explicitly declares all methods that are in IMyBaseInterfaceB in its interface body. ii) Create an interface IMyBaseInterfaceABC that extends IMyBaseInterfaceAB and explicitly declares all methods that are in IMyBaseInterfaceC in its interface body. iii) Have the following in the interfacemocks.properties file: IMyBaseInterfaceA=BaseInterfaceA IMyBaseInterfaceB=BaseInterfaceB IMyBaseInterfaceC=BaseInterfaceC IMyBaseInterfaceAB=BaseInterfaceAB:BaseInterfaceA IMyBaseInterfaceABC=BaseInterfaceABC:BaseInterfaceAB
Although it's not an ideal solution with the extra interfaces purely for mocking and having to ensure the developers take care to keep the explicitly declared methods within these extra interfaces (i.e. all the methods except for those in the first mix-in interface IMyBaseInterfaceA) in sync with any changes that may occur over time to the actual mix-in interfaces, it seems that this is the only way to do this. Would I be correct saying that?
Hope the above doesn't read as too much gibberish but your input would be welcome!
Kind regards, Donnie
On 6 September 2016 at 09:40, David F notifications@github.com wrote:
Hi Donnie,
The ApexMocks Generator has no real knowledge of Apex syntax. It parses the plain text in the static code files. Parsing text is inherently risky, so we expect the files to be in a certain format and don't attempt to traverse the interface inheritance hierarchy.
Not only would it be generally difficult to find the parent interface definition, but the parent interface might not even be in the static code - if you are extending a native interface such as System.Schedulable, or a global interface defined in another managed package.
There are ways around this however, as demonstrated in the apex-common sample app.
- interfacemocks.properties https://github.com/financialforcedev/fflib-apex-common-samplecode/blob/master/interfacemocks.properties
- fflib_SObjectMocks.cls https://github.com/financialforcedev/fflib-apex-common/blob/master/fflib/src/classes/fflib_SObjectMocks.cls
- Mocks.cls https://github.com/financialforcedev/fflib-apex-common-samplecode/blob/master/fflib-sample-code/src/classes/Mocks.cls
To put it in the context of your examples above, you should generate mocks classes for both interfaces.
interfacemocks.properties: IBaseInterface=BaseInterface ISuperInterface=SuperInterface:BaseInterface
This will generate 2 classes. The Mocks.SuperInterface extends the BaseInterface, and therefore defines a public void MyBaseMethod() method and can be mocked as normal.
I'm going to mark this case as closed, but obviously get back in touch if anything I've written here doesn't make sense!
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/financialforcedev/fflib-apex-mocks/issues/26#issuecomment-244886637, or mute the thread https://github.com/notifications/unsubscribe-auth/AD-jOo5L9jjx_q8rGC5xgM4jG6_ViU9eks5qnSbpgaJpZM4JzjVy .
Hello again, Referring back to the proposed solution in my previous email for generating mocks that implement multiple interfaces, I have just realised Apex does not allow classes to be extended unless the class is explicitly declared as virtual or abstract. Is there any syntax within the interfacemocks.properties that would allow for the generation tool to declare the generated mock class as virtual rather than having to hand-edit the generated classes manually after mock generation?
Cheers, Donnie
On 6 September 2016 at 11:12, Donnie Macleod macleoddj987@gmail.com wrote:
Excellent, thanks for that. In my second week of Salesforce development so hadn't appreciated the complexities you mentioned above. This will resolve my issue for the particular test mock classes I was wanting to generate.
I guess there is a bit of complication if we wish to mock a class that implements more than 1 interface (e.g. a class implementing mix-in interfaces) then.
For example, given we have a class MySuperClass that implements mixin interfaces IMyBaseIntefaceA, IMyBaseInterfaceB and IMyBaseInterfaceC, would I be right in thinking the only workaround for this would be as below?: i) Create an interface IMyBaseInterfaceAB that extends IMyBaseInterfaceA and explicitly declares all methods that are in IMyBaseInterfaceB in its interface body. ii) Create an interface IMyBaseInterfaceABC that extends IMyBaseInterfaceAB and explicitly declares all methods that are in IMyBaseInterfaceC in its interface body. iii) Have the following in the interfacemocks.properties file: IMyBaseInterfaceA=BaseInterfaceA IMyBaseInterfaceB=BaseInterfaceB IMyBaseInterfaceC=BaseInterfaceC IMyBaseInterfaceAB=BaseInterfaceAB:BaseInterfaceA IMyBaseInterfaceABC=BaseInterfaceABC:BaseInterfaceAB
Although it's not an ideal solution with the extra interfaces purely for mocking and having to ensure the developers take care to keep the explicitly declared methods within these extra interfaces (i.e. all the methods except for those in the first mix-in interface IMyBaseInterfaceA) in sync with any changes that may occur over time to the actual mix-in interfaces, it seems that this is the only way to do this. Would I be correct saying that?
Hope the above doesn't read as too much gibberish but your input would be welcome!
Kind regards, Donnie
On 6 September 2016 at 09:40, David F notifications@github.com wrote:
Hi Donnie,
The ApexMocks Generator has no real knowledge of Apex syntax. It parses the plain text in the static code files. Parsing text is inherently risky, so we expect the files to be in a certain format and don't attempt to traverse the interface inheritance hierarchy.
Not only would it be generally difficult to find the parent interface definition, but the parent interface might not even be in the static code - if you are extending a native interface such as System.Schedulable, or a global interface defined in another managed package.
There are ways around this however, as demonstrated in the apex-common sample app.
- interfacemocks.properties https://github.com/financialforcedev/fflib-apex-common-samplecode/blob/master/interfacemocks.properties
- fflib_SObjectMocks.cls https://github.com/financialforcedev/fflib-apex-common/blob/master/fflib/src/classes/fflib_SObjectMocks.cls
- Mocks.cls https://github.com/financialforcedev/fflib-apex-common-samplecode/blob/master/fflib-sample-code/src/classes/Mocks.cls
To put it in the context of your examples above, you should generate mocks classes for both interfaces.
interfacemocks.properties: IBaseInterface=BaseInterface ISuperInterface=SuperInterface:BaseInterface
This will generate 2 classes. The Mocks.SuperInterface extends the BaseInterface, and therefore defines a public void MyBaseMethod() method and can be mocked as normal.
I'm going to mark this case as closed, but obviously get back in touch if anything I've written here doesn't make sense!
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/financialforcedev/fflib-apex-mocks/issues/26#issuecomment-244886637, or mute the thread https://github.com/notifications/unsubscribe-auth/AD-jOo5L9jjx_q8rGC5xgM4jG6_ViU9eks5qnSbpgaJpZM4JzjVy .
Hi again Donnie,
It certainly looks like you're getting the hang of this library! I have good news and bad news, but hey, the bad news has a silver lining.
So good news first: you can mark generated classes as virtual. Put a hash in your property to control the class modifiers, in case you want to change the class visibility, sharing mode etc.
IMyBaseInterfaceA=BaseInterfaceA#public virtual with sharing
Bad news: I don't think you'll be able to generate the mock MySuperClass that implements mixin interfaces IMyBaseIntefaceA, IMyBaseInterfaceB and IMyBaseInterfaceC. As Apex doesn't allow an interface to extend more than 1 other interface, I can't see how you would make a IMyBaseInterfaceABC an instanceof IMyBaseInterfaceA+B+C.
We've encountered similar limitations internally. The inelegant, but viable, workaround is to create the mock class manually - you can use the generator to create a starting point mock class.
The good news is that you will be able to achieve this with the stub API, with no need for IMyBaseInterfaceABC. Look out for it at Dreamforce :) https://success.salesforce.com/Sessions?eventId=a1Q3000000qQOd9EAG#/session/a2q3A000000LBS8QAO
Hi David, Thanks for that, will use the hash syntax to specify the class modifiers to sort the problem then. The solution for the mixins doesn't involve extending two interfaces so the mocks for the example with the three interfaces will generate BaseInterfaceABC with all the methods from the three interfaces, the only problem being that BaseInterfaceABC instanceof IInterfaceB and BaseInterfaceABC instanceof IInterfaceC are both false (only IInterfaceA is actually extended) so it's not an ideal solution!
Looking forward to the introduction of the Stub API though to make things a lot easier for unit testing, without holding you to any specific dates would you have a rough idea when the Stub API will be generally available? Thanks again for all your help:-)
Cheers, Donnie
On 9 September 2016 at 21:41, David F notifications@github.com wrote:
Hi again Donnie,
It certainly looks like you're getting the hang of this library! I have good news and bad news, but hey, the bad news has a silver lining.
So good news first: you can mark generated classes as virtual. Put a hash in your property to control the class modifiers, in case you want to change the class visibility, sharing mode etc.
IMyBaseInterfaceA=BaseInterfaceA#public virtual with sharing
Bad news: I don't think you'll be able to generate the mock MySuperClass that implements mixin interfaces IMyBaseIntefaceA, IMyBaseInterfaceB and IMyBaseInterfaceC. As Apex doesn't allow an interface to extend more than 1 other interface, I can't see how you would make a IMyBaseInterfaceABC an instanceof IMyBaseInterfaceA+B+C.
We've encountered similar limitations internally. The inelegant, but viable, workaround is to create the mock class manually - you can use the generator to create a starting point mock class.
The good news is that you will be able to achieve this with the stub API, with no need for IMyBaseInterfaceABC. Look out for it at Dreamforce :) https://success.salesforce.com/Sessions?eventId= a1Q3000000qQOd9EAG#/session/a2q3A000000LBS8QAO
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/financialforcedev/fflib-apex-mocks/issues/26#issuecomment-246033685, or mute the thread https://github.com/notifications/unsubscribe-auth/AD-jOlO2ovt_LMl4SU0Q3cCwnjk7giMFks5qocR3gaJpZM4JzjVy .
Agree, it's not ideal but good to hear you've got something you can work with for now. Stub API is in pilot in Winter 17, so we're looking at maybe Spring/Summer 17 for general availability.
If I have an interface that extends another interface the mock generator tool only generates the methods explicitly within the sub interface for the generated mock class and does not generate methods declared in the extended super interface.
For example if I have the following two interfaces:
public interface IBaseInterface { void myBaseMethod(); }
public interface ISuperInterface extends IBaseInterface { void mySuperMethod(); }
Then the generated mock class for ISuperInterface only implements the mySuperMethod() method.