5

I've successfully implemented Named Credentials for a Basic Auth integration callout.

However, when I went to write my test method, I noticed one minor annoyance.

When I try to create a HttpCalloutMock class with the Named Credentials syntax,

req.setEndpoint('callout:My_Named_Credential/' + uri;);

I get an exception:

System.StringException: unknown protocol: callout

Also, as far as I can tell (based on the current documentation), there doesn't seem to be any way to programmatically access the properties of the Named Credential.

So, in order to set my HttpCalloutMockendpoint, I now need to hardcode the URL I entered (or put it in a custom label).

Obviously, this added dependency is not idea for maintenance.

Update

Even more frustrating... it turns out the exception is still thrown even after hardcoding the endpoint in the HttpCalloutMock.

  • also would have made a lot more sense if they did this as an actual protocol, i.e. "callout://My_Named_Credentials/some_path", if it's a valid url that seems like it would play nicer with other methods, as opposed to "callout:My_Named_Credentials/some_path", which is only a valid URI which doesn't make sense for a callout – Ralph Callaway May 7 '15 at 13:58
  • @RalphCallaway Or allowed you to actually pass the NCred object into a specific method on the request, or better, just access the data programmatically. I don't foresee this feature being useful for anyone who has strayed away from the basic auth (for example HMAC signatures) – NSjonas May 7 '15 at 16:05
  • it just means it would be more likely that other internal things don't misbehave, i.e. "Not a valid protocol", since there isn't a protocol in "callout:My_named_credential". Underneath the covers there a ton of java code intended to work with URLs so if the named credential was actually a URL I suspect there would be less annoying gotchas like you've encountered – Ralph Callaway May 8 '15 at 17:03
  • and yeah, seems like it would have made more sense to have the named credential not be endpoint specific, but instead have you set the endpoint and then .setCredential() on the request – Ralph Callaway May 8 '15 at 17:51
  • @RalphCallaway Making it endpoint specific does increase the security. It can only be sent to the endpoint it specified for. If SF is comprised, then they could post the username and password to their own system – NSjonas May 8 '15 at 17:54
5

Ahh, new Salesforce features.

That said, if you're mocking, you're not really testing any of that functionality, just how the response is handled. You're also not calling a real server, so the endpoint doesn't really matter. I'm never a fan of having to do hoops around limited Test functionality, but seems like we can make do in the interim.

So for the callout class, just change the endpoint to use or not use the named credential if you're running a test. If the replacement URL is deterministic, you can still create mock classes that work on multiple endpoints. And no hardcoding required ...

String namedCredential = 'My_Named_Credential';
String path = '/some/path';
String uri;
if(Test.isRunningTest()) {
  // would probably want to put a method for this in a TestUtil so you could get
  // to and from a mock path deterministically from a namedCredential/path combo
  uri = 'http://callout.' + namedCredential + '.com' + path; 
} else {
  uri = 'callout:' + namedCredential + path;
}
HttpRequest req = new HttpRequest();
req.setEndpoint(uri);
  • 1
    Side Note: Use a Ternary operator instead of an if/else. You'll get 100% Code Coverage. String uri = Test.isRunningTest() ? 'http://callout.' + namedCredential + '.com' + path : 'callout:' + namedCredential + path; – Scott Pelak Jul 10 '15 at 14:57
  • 1
    Also, if you use the MultiStaticResourceCalloutMock version, it does seem to let you set an endpoint like multimock.setStaticResource('callout:Google_Analytics...') – BritishBoyinDC Jan 18 '18 at 20:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.