function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
SinanBunniSinanBunni 

HTTP and Basic Callout Trailhead Challange

Hello,

I am trying to solve the challange of the HTTP and Basic Callout module of Salesforce trailhead and I am having some queries in which you could point to the right direction:

1- The challange asked us to call this URL https://th-apex-http-callout.herokuapp.com/animals/:id in method getAnimalNameById... should that URL be in this form instead of the above https://th-apex-http-callout.herokuapp.com/animals?id ? Where id is a parameter in the URL.

2- When I tried to check the solution of the challange, Salesforce generated that error for me
"Challenge Not yet complete... here's what's wrong: 
Executing the 'getAnimalNameById' method on 'AnimalLocator' failed. Make sure the method exists with the name 'getAnimalNameById', is public and static, accepts an Integer and returns a String."
despite that my class implementation has this method declared as public static String as below in the code snippet:
 
public class AnimalLocator {
	
	public static String getAnimalNameById(Integer id) {
		
		Http http = new Http();
		HttpRequest request = new HttpRequest();
		request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals?id');
		request.setMethod('GET');
		
		HttpResponse response = http.send(request);
		List<Object> animals = NULL;
		String returnValue = NULL;
		
		// if the request was successful, then parse the JSON response
		if (response.getStatusCode() == 200) {
			Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
			animals = (List<Object>) result.get('animals');
			System.debug(animals);
		}
		
		if (animals.size() > 0 && animals != NULL && id < animals.size()) {
			returnValue = (String) animals.get(id);
		}
		
		return returnValue;
	} // end getAnimalNameById method
    
} // end AnimalLocator class

I would appreciate your help in this post.

Thank you,

Sinan
Best Answer chosen by SinanBunni
pconpcon
No, you should be replacing the Id at the end of the url with the id passed into the method

Try
 
request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/' + id);

instead

All Answers

pconpcon
No, you should be replacing the Id at the end of the url with the id passed into the method

Try
 
request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/' + id);

instead
This was selected as the best answer
SinanBunniSinanBunni
Hello pcon,

Thank you for your prompt reply. That tip helped me to solve the problem I am facing and getting the 500 points of the challange. Thanks. Sinan
Ajay Ghuge 6Ajay Ghuge 6
Hi There ,

I wrote the class in the following manner : 
 
public class AnimalLocator
{

  public static String getAnimalNameById(Integer id)
   {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/'+id);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        String strResp = '';
         
        if (response.getStatusCode() == 200) {
            // Deserializes the JSON string into collections of primitive data types.
       Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
           Object animals = (Object) results.get('name');
          strResp +=string.valueof(animals ) ;
        }
        return strResp ;
   }
  
}

But facing 

Challenge Not yet complete... here's what's wrong: 
Executing the 'getAnimalNameById' method on 'AnimalLocator' failed. Make sure the method exists with the name 'getAnimalNameById', is public and static, accepts an Integer and returns a String.

Can anyone explain what is wrong in this ?

Regards,
Ajay 
pconpcon
Ajay: Not sure why your are getting this.  Having the following works for me
 
public class AnimalLocator {
    public class Animal {
        public Integer id;
        public String name;
        public String eats;
        public String says;
    }
    
    public class AnimalResult {
        public Animal animal;
    }

	public static String getAnimalNameById(Integer id) {
        Http http = new Http();
        
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/' + id);
        request.setMethod('GET');
        
        HttpResponse response = http.send(request);
        AnimalResult result = (AnimalResult) JSON.deserialize(response.getBody(), AnimalResult.class);
        return result.animal.name;
   }
}

If you open the developer console and run the following what do you get back?
 
System.debug(AnimalLocator.getAnimalNameById(1));
Ajay Ghuge 6Ajay Ghuge 6
Hi Pcon ,

Following code works for me after checking the response.
 
public class AnimalLocator
{

  public static String getAnimalNameById(Integer id)
   {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/'+id);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
          String strResp = '';
           system.debug('******response '+response.getStatusCode());
           system.debug('******response '+response.getBody());
        // If the request is successful, parse the JSON response.
        if (response.getStatusCode() == 200) 
        {
            // Deserializes the JSON string into collections of primitive data types.
           Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
            // Cast the values in the 'animals' key as a list
           Map<string,object> animals = (map<string,object>) results.get('animal');
            System.debug('Received the following animals:' + animals );
            strResp = string.valueof(animals.get('name'));
            System.debug('strResp >>>>>>' + strResp );
        }
        return strResp ;
   }
  
}

Thanks for your help !!! 

Regards,
Ajay 
Sai Sasidhar BhagavathulaSai Sasidhar Bhagavathula
Hi,

I've written the code in the below format.


public class AnimalLocator {    
    public class Animal{
        public Animal Animal1;
    }
    public class Animal1 {
        public Integer id;
        public String name;
        public String eats;
        public String says;
    }        
    public static String getAnimalNameById(Integer id) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/' + id);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        Animal anm = (Animal) JSON.deserialize(response.getBody(), Animal.class);
        return anm.Animal1.name;
 }
}

But I'm getting error as "Variable does not exist: name"

Can you guys plz help me out on this.....

Thanks in Advance !!!
pconpcon
Your problem is the declaration of your class. Right now your Animal class had a circular declaration since animal1 is of type Animal. See my reply above for the class declaration and deserialization
Sai Sasidhar BhagavathulaSai Sasidhar Bhagavathula
Sorry...Can you please elaborate what do you mean by circular declaration please.

Thanks in Advance !!!
pconpcon
What you are trying to do is have a variable named animal that is a reference to your Animal1 class. It needs to be named animal so that it can be deserialized into the object. Right now you have a variable named Animal1 that points to your Animal class that has a variable named Animal1 that points up your Animal class that has a variable... In a circle. What you want to do is say
 
public Animal1 animal;

This will define a variable named animal that points up your Animal1 class that has all of your expected fields.  Then you would reference name by saying
 
an.animal.name

 
Sai Sasidhar BhagavathulaSai Sasidhar Bhagavathula
Thanks for detailed explanation :)

Got it and it resolved my issue... 
Sai Sasidhar BhagavathulaSai Sasidhar Bhagavathula
Hi,

I've written test class after checking challenge facing the issue as per below.
Could you please check

Code :

public class AnimalLocator {    

    public class Animal {
        public Integer id;
        public String name;
        public String eats;
        public String says;
    }        
    public static String getAnimalNameById(Integer id) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/' + id);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        Animal anm = (Animal) JSON.deserialize(response.getBody(),Animal.class);
        string st = anm.name;
        return st;
 }
}

Error :

Challenge not yet complete
Executing the 'getAnimalNameById' method on 'AnimalLocator' failed. Make sure the method exists with the name 'getAnimalNameById', is public and static, accepts an Integer and returns a String.

Thanks in Advance !!

 
pconpcon
If you run it in the developer console, does the method work as expected?  My guess is no.  Because your deserialization is wrong.  As stated before, you need the wrapper to match the object structure coming back from the JSON data.  I would recommend reading over this article [1] about JSON deserialization in Apex to understand a little bit clearer.  However, if you visit one of the animal records, you will see this data returned
 
{
    "animal": {
        "id": 1,
        "name": "chicken",
        "eats": "chicken food",
        "says": "cluck cluck"
     }
}

This has a variable named "animal" that you have to have in your main class.  Then that variable contains data about the animal.
 
public class Animal {
    public Integer Id
    public String name;
    public String eats;
    public String says;
}

public class AnimalWrapper {
    public Animal animal;
}

This allows you deserialize the data returned from the result as expected.  By using
 
AnimalWrapper wrapper = (AnimalWrapper) JSON.deserialize(response.getBody(), AnimalWrapper.class);
return wrapper.animal.name;

​​​​​​​NOTE: When adding code please use the "Add a code sample" button (icon <>) to increase readability and make it easier to reference.
vikas gupta 93vikas gupta 93
Try this by using JSONParser, this works perfect!!!
Animal Locator:
public class AnimalLocator {
    public static String getAnimalNameById(Integer id) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/'+id);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
       	/*Map<String,Object> results = (Map<String,Object>)JSON.deserializeUntyped(response.getBody());
        system.debug('---->results'+results);
        List<Object> animals = (List<Object>) results.get('animal');
        system.debug('---->animal'+animals);*/
        Map<Integer,String> mapAnimal = new Map<Integer,String>();
        Integer varId;
        String varName;
        JSONParser parser1= JSON.createParser(response.getBody());
        while (parser1.nextToken() != null) {
            if ((parser1.getCurrentToken() == JSONToken.FIELD_NAME) && (parser1.getText() == 'id')) {
                // Get the value.
                parser1.nextToken();
                // Fetch the ids for all animals in JSON Response.
                varId=parser1.getIntegerValue();
                System.debug('---->varId-->'+varID);
                parser1.nextToken();
            }
            if ((parser1.getCurrentToken() == JSONToken.FIELD_NAME) && (parser1.getText() == 'name')) {
                parser1.nextToken();
                // Fetch the names for all animals in JSON Response.
                varName=parser1.getText();
                System.debug('---->varName-->'+varName);
            }
            mapAnimal.put(varId,varName);
        }
        system.debug('---->mapAnimal-->'+mapAnimal);
        return mapAnimal.get(id); 
        
    }
}

Mock Test Class:
@isTest
global class AnimalLocatorMock implements HttpCalloutMock {
    // Implement this interface method
    global HTTPResponse respond(HTTPRequest request) {
        // Create a fake response
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"animal":[{"id":1,"name":"chicken","eats":"chicken food","says":"cluck cluck"},{"id":2,"name":"duck","eats":"worms","says":"pek pek"}]}');
        response.setStatusCode(200);
        return response; 
    }
}
Test Class:
@isTest
private class AnimalLocatorTest {
@isTest static void testGetCallout() {
    // Set mock callout class 
    Test.setMock(HttpCalloutMock.class, new AnimalLocatorMock()); 
    // This causes a fake response to be sent
    // from the class that implements HttpCalloutMock. 
    String response = AnimalLocator.getAnimalNameById(1);
    system.debug('Test Response1--->'+response);
    String expectedValue = 'chicken';
    System.assertEquals(expectedValue,response);
    String response2 = AnimalLocator.getAnimalNameById(2);
    system.debug('Test Response2--->'+response2);
    String expectedValue2 = 'duck';
    System.assertEquals(expectedValue2,response2);
}
}

Sharing is caring!!!!
 
pconpcon
I would really recommend against using the JSON.createParser methods.  It is very difficult to read and debug.  If you do not want to use the typed deserialization you can use untyped deserialization but I would recommend against that too as it can be problematic if the data does not exist as expected.
 
Map<String, Object> jsonData = (Map<String, Object>) JSON.deserializeUntyped(resposne.getBody());
Map<String, Object> animalData = (Map<String, Object>) jsonData.get('animal');
return (String) animalData.get('name');

This does the same thing as the createParser does above (non-typed deserialization) but does it in a manner that is cleaner and easier to read.  In production you'd want to have checks to make sure that the animal key in the jsonData map exists before trying to get the name key.
Siri Chandana LingamguntaSiri Chandana Lingamgunta
Apex Class : 
public class AnimalLocator {
    public static String getAnimalNameById(Integer id){
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/'+id);
        request.setMethod('GET');
        System.debug('>>>>>>>'+id);
        HttpResponse response = http.send(request);
       Object animals; 
        String returnValue; 
        
        // parse the JSON response
        if (response.getStatusCode() == 200) {
            Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
            System.debug('>>>>>'+response.getBody());
            animals = result.get('animal');
            System.debug(animals);
        }
        if (animals != NULL  ) {
            Map<String, Object> result1 = (Map<String, Object>) JSON.deserializeUntyped(JSON.serialize(animals));
            returnValue =(String) result1.get('name');
        }
        return returnValue;
    } 
}

Test Class :
@isTest
global class AnimalLocatorTest {
@isTest static void AnimalLocatorMock1() {
        Test.setMock(HttpCalloutMock.class, new AnimalLocatorMock());
        string result = AnimalLocator.getAnimalNameById(3);
        String expectedResult = 'chicken';
        //System.assertEquals(result,expectedResult );
    }
}
Mock Test : 
@isTest
global class AnimalLocatorMock implements HttpCalloutMock{
     global HTTPResponse respond(HTTPRequest request) {
        // Create a fake response
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"animal":         {"id":99,"name":"trailhead","eats":"burritos","says":"more badgers"}}');
        response.setStatusCode(200);
        return response; 
    }
}
Ivo Stoykov 9Ivo Stoykov 9
Hi all,

I faced this error and cannot find why it pops up... regardless in the log is visible that the method returns a string as requested. Any idea would be appreciated.

Challenge not yet complete in My Trailhead Playground 2
Executing the 'getAnimalNameById' method on 'AnimalLocator' failed. Make sure the method exists with the name 'getAnimalNameById', is public and static, accepts an Integer and returns a String.

The class
public class AnimalLocator {
    public static String getAnimalNameById(Integer id){
        String uri = 'https://th-apex-http-callout.herokuapp.com/animals/' + id;

        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(uri);
        request.setMethod('GET');
        System.debug(String.valueOf(request));
        HttpResponse response = http.send(request);
        System.debug(String.valueOf(response));
        Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
        System.debug('results: ' + JSON.serialize(results));
        System.debug('name: ' + results.get('name'));
        return String.valueOf(results.get('name'));

    }
}
The mock
@IsTest
global class AnimalLocatorMock implements HttpCalloutMock {
    
    global HTTPResponse respond(HTTPRequest request){
        System.debug(String.valueOf(this));
        System.debug(String.valueOf(request));
        if(request == null){
            request = new HTTPRequest();
        }
        System.debug(String.valueOf(request));
        HttpResponse response = new HttpResponse();
        System.debug(String.valueOf(response));
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"id": 1, "name": "Goophy"}');
        response.setStatusCode(200);
        return response; 
    }
}
The test
@IsTest
public class AnimalLocatorTest {

    @IsTest
    public static void getAnimalNameByIdTest(){
    	Test.setMock(HttpCalloutMock.class, new AnimalLocatorMock()); 
        //AnimalsCallouts.makeGetCallout();
        String name = AnimalLocator.getAnimalNameById(1);
        System.assertEquals('Goophy', name);

    }
}

And the log (sorry it is a bit long)
46.0 APEX_CODE,FINEST;APEX_PROFILING,ERROR;CALLOUT,ERROR;DB,INFO;NBA,ERROR;SYSTEM,FINEST;VALIDATION,ERROR;VISUALFORCE,FINE;WAVE,ERROR;WORKFLOW,ERROR
16:41:38.0 (784118)|USER_INFO|[EXTERNAL]|0054I000005d5pV|ivo.stoykov@cunning-wolf-pbmzu5.com|(GMT+01:00) British Summer Time (Europe/London)|GMT+01:00
16:41:38.0 (879221)|EXECUTION_STARTED
16:41:38.0 (884822)|CODE_UNIT_STARTED|[EXTERNAL]|01p4I00000AECnk|AnimalLocatorTest.getAnimalNameByIdTest()
16:41:38.0 (1737787)|HEAP_ALLOCATE|[72]|Bytes:3
16:41:38.0 (1820528)|HEAP_ALLOCATE|[77]|Bytes:152
16:41:38.0 (1842749)|HEAP_ALLOCATE|[342]|Bytes:408
16:41:38.0 (1862952)|HEAP_ALLOCATE|[355]|Bytes:408
16:41:38.0 (1878561)|HEAP_ALLOCATE|[467]|Bytes:48
16:41:38.0 (1912513)|HEAP_ALLOCATE|[139]|Bytes:6
16:41:38.0 (1972860)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:5
16:41:38.0 (2036155)|METHOD_ENTRY|[2]|01p4I00000AECnk|AnimalLocatorTest.AnimalLocatorTest()
16:41:38.0 (2048597)|STATEMENT_EXECUTE|[2]
16:41:38.0 (2055535)|STATEMENT_EXECUTE|[2]
16:41:38.0 (2076167)|METHOD_EXIT|[2]|AnimalLocatorTest
16:41:38.0 (2141099)|HEAP_ALLOCATE|[50]|Bytes:5
16:41:38.0 (2169529)|HEAP_ALLOCATE|[56]|Bytes:5
16:41:38.0 (2179735)|HEAP_ALLOCATE|[64]|Bytes:7
16:41:38.0 (2215660)|STATEMENT_EXECUTE|[5]
16:41:38.0 (2218588)|STATEMENT_EXECUTE|[6]
16:41:38.0 (2222308)|HEAP_ALLOCATE|[6]|Bytes:22
16:41:38.0 (2301423)|HEAP_ALLOCATE|[6]|Bytes:27
16:41:38.0 (2315189)|SYSTEM_METHOD_ENTRY|[1]|Type.Type()
16:41:38.0 (2321764)|STATEMENT_EXECUTE|[1]
16:41:38.0 (2329517)|SYSTEM_METHOD_EXIT|[1]|Type
16:41:38.0 (2338816)|HEAP_ALLOCATE|[6]|Bytes:8
16:41:38.0 (2354273)|HEAP_ALLOCATE|[6]|Bytes:8
16:41:38.0 (2372173)|VARIABLE_SCOPE_BEGIN|[4]|this|System.Type|true|false
16:41:38.0 (2471649)|VARIABLE_ASSIGNMENT|[4]|this|{}|0x1d4c71cf
16:41:38.0 (2483202)|VARIABLE_SCOPE_BEGIN|[4]|apextype|java:common.apex.runtime.ApexObjectType|true|false
16:41:38.0 (2664870)|VARIABLE_ASSIGNMENT|[4]|apextype|"System.HttpCalloutMock"|0x58dd6878
16:41:38.0 (5006082)|HEAP_ALLOCATE|[6]|Bytes:8
16:41:38.0 (7027194)|HEAP_ALLOCATE|[6]|Bytes:1
16:41:38.0 (7045952)|HEAP_ALLOCATE|[6]|Bytes:9
16:41:38.0 (7070980)|METHOD_ENTRY|[2]|01p4I00000AECnp|AnimalLocatorMock.AnimalLocatorMock()
16:41:38.0 (7080331)|STATEMENT_EXECUTE|[2]
16:41:38.0 (7088189)|STATEMENT_EXECUTE|[2]
16:41:38.0 (7093470)|METHOD_EXIT|[2]|AnimalLocatorMock
16:41:38.0 (7111443)|HEAP_ALLOCATE|[6]|Bytes:4
16:41:38.0 (7136345)|CONSTRUCTOR_ENTRY|[6]|01p4I00000AECnp|<init>()|AnimalLocatorMock
16:41:38.0 (7193634)|VARIABLE_SCOPE_BEGIN|[2]|this|AnimalLocatorMock|true|false
16:41:38.0 (7242297)|VARIABLE_ASSIGNMENT|[2]|this|{}|0x48d77269
16:41:38.0 (7269183)|STATEMENT_EXECUTE|[2]
16:41:38.0 (7279490)|CONSTRUCTOR_EXIT|[6]|01p4I00000AECnp|<init>()|AnimalLocatorMock
16:41:38.0 (7336198)|HEAP_ALLOCATE|[6]|Bytes:74
16:41:38.0 (7347508)|SYSTEM_METHOD_ENTRY|[1]|Test.Test()
16:41:38.0 (7352704)|STATEMENT_EXECUTE|[1]
16:41:38.0 (7358911)|SYSTEM_METHOD_EXIT|[1]|Test
16:41:38.0 (7397422)|METHOD_ENTRY|[6]||System.Test.setMock(System.Type, Object)
16:41:38.0 (8426270)|METHOD_EXIT|[6]||System.Test.setMock(System.Type, Object)
16:41:38.0 (8442598)|STATEMENT_EXECUTE|[8]
16:41:38.0 (10296189)|HEAP_ALLOCATE|[8]|Bytes:1
16:41:38.0 (10316841)|HEAP_ALLOCATE|[8]|Bytes:10
16:41:38.0 (10336178)|METHOD_ENTRY|[1]|01p4I00000AECnf|AnimalLocator.AnimalLocator()
16:41:38.0 (10344816)|STATEMENT_EXECUTE|[1]
16:41:38.0 (10350212)|STATEMENT_EXECUTE|[1]
16:41:38.0 (10356848)|METHOD_EXIT|[1]|AnimalLocator
16:41:38.0 (10384251)|METHOD_ENTRY|[8]|01p4I00000AECnf|AnimalLocator.getAnimalNameById(Integer)
16:41:38.0 (10413806)|VARIABLE_SCOPE_BEGIN|[2]|id|Integer|false|false
16:41:38.0 (10437918)|VARIABLE_ASSIGNMENT|[2]|id|1
16:41:38.0 (10453893)|STATEMENT_EXECUTE|[2]
16:41:38.0 (10456516)|STATEMENT_EXECUTE|[3]
16:41:38.0 (10461524)|HEAP_ALLOCATE|[3]|Bytes:51
16:41:38.0 (10513508)|SYSTEM_METHOD_ENTRY|[3]|String.valueOf(Object)
16:41:38.0 (10544193)|HEAP_ALLOCATE|[3]|Bytes:1
16:41:38.0 (10558181)|SYSTEM_METHOD_EXIT|[3]|String.valueOf(Object)
16:41:38.0 (10570895)|HEAP_ALLOCATE|[3]|Bytes:52
16:41:38.0 (10585777)|VARIABLE_SCOPE_BEGIN|[3]|uri|String|false|false
16:41:38.0 (10608436)|VARIABLE_ASSIGNMENT|[3]|uri|"https://th-apex-http (32 more) ..."
16:41:38.0 (10617873)|STATEMENT_EXECUTE|[5]
16:41:38.0 (10637440)|VARIABLE_SCOPE_BEGIN|[5]|http|System.Http|true|false
16:41:38.0 (10709024)|VARIABLE_ASSIGNMENT|[5]|http|"System.Http[]"|0x39b254b9
16:41:38.0 (10718833)|STATEMENT_EXECUTE|[6]
16:41:38.0 (10727234)|VARIABLE_SCOPE_BEGIN|[6]|request|System.HttpRequest|true|false
16:41:38.0 (10767718)|VARIABLE_ASSIGNMENT|[6]|request|"System.HttpRequest[Endpoint=null, Method=null]"|0x5585f791
16:41:38.0 (10776900)|STATEMENT_EXECUTE|[7]
16:41:38.0 (10818322)|SYSTEM_METHOD_ENTRY|[7]|System.HttpRequest.setEndpoint(String)
16:41:38.0 (10839089)|SYSTEM_METHOD_EXIT|[7]|System.HttpRequest.setEndpoint(String)
16:41:38.0 (10845705)|STATEMENT_EXECUTE|[8]
16:41:38.0 (10850079)|HEAP_ALLOCATE|[8]|Bytes:3
16:41:38.0 (10864275)|SYSTEM_METHOD_ENTRY|[8]|System.HttpRequest.setMethod(String)
16:41:38.0 (10876926)|SYSTEM_METHOD_EXIT|[8]|System.HttpRequest.setMethod(String)
16:41:38.0 (10882605)|STATEMENT_EXECUTE|[9]
16:41:38.0 (10906094)|SYSTEM_METHOD_ENTRY|[9]|String.valueOf(Object)
16:41:38.0 (10919467)|HEAP_ALLOCATE|[9]|Bytes:93
16:41:38.0 (10932196)|SYSTEM_METHOD_EXIT|[9]|String.valueOf(Object)
16:41:38.0 (10951547)|SYSTEM_METHOD_ENTRY|[9]|System.debug(ANY)
16:41:38.0 (10965253)|USER_DEBUG|[9]|DEBUG|System.HttpRequest[Endpoint=https://th-apex-http-callout.herokuapp.com/animals/1, Method=GET]
16:41:38.0 (10974996)|SYSTEM_METHOD_EXIT|[9]|System.debug(ANY)
16:41:38.0 (10980976)|STATEMENT_EXECUTE|[10]
16:41:38.0 (10989002)|SYSTEM_METHOD_ENTRY|[10]|System.Http.send(ANY)
16:41:38.0 (11175854)|HEAP_ALLOCATE|[10]|Bytes:4
16:41:38.0 (11191822)|HEAP_ALLOCATE|[10]|Bytes:52
16:41:38.0 (11202981)|VARIABLE_SCOPE_BEGIN|[4]|this|AnimalLocatorMock|true|false
16:41:38.0 (11233704)|VARIABLE_ASSIGNMENT|[4]|this|{}|0x48d77269
16:41:38.0 (11245013)|VARIABLE_SCOPE_BEGIN|[4]|request|System.HttpRequest|true|false
16:41:38.0 (11301351)|VARIABLE_ASSIGNMENT|[4]|request|"System.HttpRequest[Endpoint=https://th-apex-http-callout.herokuapp.com/animals/1, Method=GET]"|0x5585f791
16:41:38.0 (11330358)|STATEMENT_EXECUTE|[4]
16:41:38.0 (11333115)|STATEMENT_EXECUTE|[5]
16:41:38.0 (11350024)|SYSTEM_METHOD_ENTRY|[5]|String.valueOf(Object)
16:41:38.0 (11367494)|HEAP_ALLOCATE|[5]|Bytes:20
16:41:38.0 (11376662)|SYSTEM_METHOD_EXIT|[5]|String.valueOf(Object)
16:41:38.0 (11385429)|SYSTEM_METHOD_ENTRY|[5]|System.debug(ANY)
16:41:38.0 (11390797)|USER_DEBUG|[5]|DEBUG|AnimalLocatorMock:[]
16:41:38.0 (11396730)|SYSTEM_METHOD_EXIT|[5]|System.debug(ANY)
16:41:38.0 (11401035)|STATEMENT_EXECUTE|[6]
16:41:38.0 (11408931)|SYSTEM_METHOD_ENTRY|[6]|String.valueOf(Object)
16:41:38.0 (11416809)|HEAP_ALLOCATE|[6]|Bytes:93
16:41:38.0 (11426772)|SYSTEM_METHOD_EXIT|[6]|String.valueOf(Object)
16:41:38.0 (11433190)|SYSTEM_METHOD_ENTRY|[6]|System.debug(ANY)
16:41:38.0 (11437269)|USER_DEBUG|[6]|DEBUG|System.HttpRequest[Endpoint=https://th-apex-http-callout.herokuapp.com/animals/1, Method=GET]
16:41:38.0 (11442883)|SYSTEM_METHOD_EXIT|[6]|System.debug(ANY)
16:41:38.0 (11447391)|STATEMENT_EXECUTE|[7]
16:41:38.0 (11450173)|STATEMENT_EXECUTE|[10]
16:41:38.0 (11457856)|SYSTEM_METHOD_ENTRY|[10]|String.valueOf(Object)
16:41:38.0 (11465388)|HEAP_ALLOCATE|[10]|Bytes:93
16:41:38.0 (11473165)|SYSTEM_METHOD_EXIT|[10]|String.valueOf(Object)
16:41:38.0 (11478909)|SYSTEM_METHOD_ENTRY|[10]|System.debug(ANY)
16:41:38.0 (11483001)|USER_DEBUG|[10]|DEBUG|System.HttpRequest[Endpoint=https://th-apex-http-callout.herokuapp.com/animals/1, Method=GET]
16:41:38.0 (11488496)|SYSTEM_METHOD_EXIT|[10]|System.debug(ANY)
16:41:38.0 (11492349)|STATEMENT_EXECUTE|[11]
16:41:38.0 (11506700)|VARIABLE_SCOPE_BEGIN|[11]|response|System.HttpResponse|true|false
16:41:38.0 (11544143)|VARIABLE_ASSIGNMENT|[11]|response|"System.HttpResponse[Status=null, StatusCode=0]"|0x2a495d47
16:41:38.0 (11550648)|STATEMENT_EXECUTE|[12]
16:41:38.0 (11559899)|SYSTEM_METHOD_ENTRY|[12]|String.valueOf(Object)
16:41:38.0 (11569095)|HEAP_ALLOCATE|[12]|Bytes:46
16:41:38.0 (11577460)|SYSTEM_METHOD_EXIT|[12]|String.valueOf(Object)
16:41:38.0 (11583988)|SYSTEM_METHOD_ENTRY|[12]|System.debug(ANY)
16:41:38.0 (11588286)|USER_DEBUG|[12]|DEBUG|System.HttpResponse[Status=null, StatusCode=0]
16:41:38.0 (11593748)|SYSTEM_METHOD_EXIT|[12]|System.debug(ANY)
16:41:38.0 (11597735)|STATEMENT_EXECUTE|[13]
16:41:38.0 (11601553)|HEAP_ALLOCATE|[13]|Bytes:12
16:41:38.0 (11606472)|HEAP_ALLOCATE|[13]|Bytes:16
16:41:38.0 (11629778)|SYSTEM_METHOD_ENTRY|[13]|System.HttpResponse.setHeader(String, String)
16:41:38.0 (11656148)|SYSTEM_METHOD_EXIT|[13]|System.HttpResponse.setHeader(String, String)
16:41:38.0 (11661187)|STATEMENT_EXECUTE|[14]
16:41:38.0 (11664819)|HEAP_ALLOCATE|[14]|Bytes:27
16:41:38.0 (11674815)|SYSTEM_METHOD_ENTRY|[14]|System.HttpResponse.setBody(String)
16:41:38.0 (11700369)|SYSTEM_METHOD_EXIT|[14]|System.HttpResponse.setBody(String)
16:41:38.0 (11704885)|STATEMENT_EXECUTE|[15]
16:41:38.0 (11718178)|SYSTEM_METHOD_ENTRY|[15]|System.HttpResponse.setStatusCode(Integer)
16:41:38.0 (11727560)|SYSTEM_METHOD_EXIT|[15]|System.HttpResponse.setStatusCode(Integer)
16:41:38.0 (11731592)|STATEMENT_EXECUTE|[16]
16:41:38.0 (11746146)|HEAP_ALLOCATE|[10]|Bytes:31
16:41:38.0 (11752163)|SYSTEM_METHOD_EXIT|[10]|System.Http.send(ANY)
16:41:38.0 (11757500)|VARIABLE_SCOPE_BEGIN|[10]|response|System.HttpResponse|true|false
16:41:38.0 (11787139)|VARIABLE_ASSIGNMENT|[10]|response|"System.HttpResponse[Status=null, StatusCode=200]"|0x2a495d47
16:41:38.0 (11793518)|STATEMENT_EXECUTE|[11]
16:41:38.0 (11805494)|SYSTEM_METHOD_ENTRY|[11]|String.valueOf(Object)
16:41:38.0 (11815090)|HEAP_ALLOCATE|[11]|Bytes:48
16:41:38.0 (11823379)|SYSTEM_METHOD_EXIT|[11]|String.valueOf(Object)
16:41:38.0 (11830320)|SYSTEM_METHOD_ENTRY|[11]|System.debug(ANY)
16:41:38.0 (11834743)|USER_DEBUG|[11]|DEBUG|System.HttpResponse[Status=null, StatusCode=200]
16:41:38.0 (11840518)|SYSTEM_METHOD_EXIT|[11]|System.debug(ANY)
16:41:38.0 (11844310)|STATEMENT_EXECUTE|[12]
16:41:38.0 (11851314)|SYSTEM_METHOD_ENTRY|[12]|System.HttpResponse.getBody()
16:41:38.0 (11865468)|HEAP_ALLOCATE|[12]|Bytes:27
16:41:38.0 (11870102)|SYSTEM_METHOD_EXIT|[12]|System.HttpResponse.getBody()
16:41:38.0 (11927199)|HEAP_ALLOCATE|[12]|Bytes:24
16:41:38.0 (11941330)|SYSTEM_METHOD_ENTRY|[1]|JSON.JSON()
16:41:38.0 (11948486)|STATEMENT_EXECUTE|[1]
16:41:38.0 (11960611)|SYSTEM_METHOD_EXIT|[1]|JSON
16:41:38.0 (11977163)|METHOD_ENTRY|[12]||System.JSON.deserializeUntyped(String)
16:41:38.0 (12273201)|METHOD_EXIT|[12]||System.JSON.deserializeUntyped(String)
16:41:38.0 (12288405)|VARIABLE_SCOPE_BEGIN|[12]|results|Map<String,ANY>|true|false
16:41:38.0 (12321238)|VARIABLE_ASSIGNMENT|[12]|results|{"id":1,"name":"Goophy"}|0x5e801fbb
16:41:38.0 (12330918)|STATEMENT_EXECUTE|[13]
16:41:38.0 (12335059)|HEAP_ALLOCATE|[13]|Bytes:9
16:41:38.0 (12351546)|METHOD_ENTRY|[13]||System.JSON.serialize(Object)
16:41:38.0 (12893985)|METHOD_EXIT|[13]||System.JSON.serialize(Object)
16:41:38.0 (12911420)|HEAP_ALLOCATE|[13]|Bytes:33
16:41:38.0 (12923754)|SYSTEM_METHOD_ENTRY|[13]|System.debug(ANY)
16:41:38.0 (12929794)|USER_DEBUG|[13]|DEBUG|results: {"name":"Goophy","id":1}
16:41:38.0 (12936567)|SYSTEM_METHOD_EXIT|[13]|System.debug(ANY)
16:41:38.0 (12941665)|STATEMENT_EXECUTE|[14]
16:41:38.0 (12946185)|HEAP_ALLOCATE|[14]|Bytes:6
16:41:38.0 (12951728)|HEAP_ALLOCATE|[14]|Bytes:4
16:41:38.0 (12978519)|SYSTEM_METHOD_ENTRY|[14]|Map<String,ANY>.get(Object)
16:41:38.0 (13001804)|SYSTEM_METHOD_EXIT|[14]|Map<String,ANY>.get(Object)
16:41:38.0 (13017548)|SYSTEM_METHOD_ENTRY|[14]|String.valueOf(Object)
16:41:38.0 (13024546)|HEAP_ALLOCATE|[14]|Bytes:6
16:41:38.0 (13042905)|SYSTEM_METHOD_EXIT|[14]|String.valueOf(Object)
16:41:38.0 (13054909)|HEAP_ALLOCATE|[14]|Bytes:12
16:41:38.0 (13069464)|SYSTEM_METHOD_ENTRY|[14]|System.debug(ANY)
16:41:38.0 (13083864)|USER_DEBUG|[14]|DEBUG|name: Goophy
16:41:38.0 (13095914)|SYSTEM_METHOD_EXIT|[14]|System.debug(ANY)
16:41:38.0 (13105151)|STATEMENT_EXECUTE|[15]
16:41:38.0 (13118446)|SYSTEM_METHOD_ENTRY|[15]|Map<String,ANY>.get(Object)
16:41:38.0 (13147524)|SYSTEM_METHOD_EXIT|[15]|Map<String,ANY>.get(Object)
16:41:38.0 (13173032)|SYSTEM_METHOD_ENTRY|[15]|String.valueOf(Object)
16:41:38.0 (13180739)|HEAP_ALLOCATE|[15]|Bytes:6
16:41:38.0 (13203231)|SYSTEM_METHOD_EXIT|[15]|String.valueOf(Object)
16:41:38.0 (13213673)|METHOD_EXIT|[8]|01p4I00000AECnf|AnimalLocator.getAnimalNameById(Integer)
16:41:38.0 (13223015)|VARIABLE_SCOPE_BEGIN|[8]|name|String|false|false
16:41:38.0 (13243471)|VARIABLE_ASSIGNMENT|[8]|name|"Goophy"
16:41:38.0 (13251930)|STATEMENT_EXECUTE|[9]
16:41:38.0 (13258044)|HEAP_ALLOCATE|[9]|Bytes:6
16:41:38.0 (13286131)|SYSTEM_METHOD_ENTRY|[9]|System.assertEquals(ANY, ANY)
16:41:38.0 (13328932)|SYSTEM_METHOD_EXIT|[9]|System.assertEquals(ANY, ANY)
16:41:38.0 (13356449)|CODE_UNIT_FINISHED|AnimalLocatorTest.getAnimalNameByIdTest()
16:41:38.0 (14580480)|EXECUTION_FINISHED



 
BenitoVBenitoV
I think the reason is the structure of the JSON, it must look like this one:
{ "animal": { "id": 1, "name": "chicken", "eats": "chicken food", "says": "cluck cluck" } }

And you need to change the code to parse it corretly.
Jennifer Do 9Jennifer Do 9

{ "animal": { "id": 1, "name": "chicken", "eats": "chicken food", "says": "cluck cluck" } }

this worked for me too!

NipuWNipuW
Following worked for me. Here i am not using JSON parser or JSON.deserialize() with a seperate mapping class. But simply using another Map to get the inner object by name. 

public class AnimalLocator {
    
    public static String getAnimalNameById(Integer animalId) {
        String animalName;
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/'+animalId);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        // If the request is successful, parse the JSON response.
        if (response.getStatusCode() == 200) {
            // Deserializes the JSON string into collections of primitive data types.
            Map<String, Object> animalList = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
            Map<String, Object> animal = (Map<String, Object>) animalList.get('animal');
            animalName =  (String)animal.get('name');   
            System.debug(animalList);
        }
        return animalName;
    }

nikolai calder 14nikolai calder 14
Keep getting an error in trailhead even though my test has no failure someone please help
here is the code 
 
public class AnimalLocator {
    public static String getAnimalById(Integer num){
        String animalName;
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/'+num);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        if (response.getStatusCode() == 200) {
            Map<String, Object> r = (Map<String, Object>)
                JSON.deserializeUntyped(response.getbody());
            Map<String, Object> animal = (Map<String, Object>)r.get('animal');
            animalName = string.valueof(animal.get('name'));
            
        }
        return animalName;
    }
}



@isTest
global class AnimalLocatorMock implements HttpCalloutMock {
    // Implement this interface method
    global HTTPResponse respond(HTTPRequest request) {
        // Create a fake response
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"animal":{"id":1,"name":"chicken","eats":"chicken food","says":"cluck cluck"}}');
        response.setStatusCode(200);
        return response; 
    }
}



@isTest
private class AnimalLocatorTest{
@isTest static void getAnimalByIdTest () {
    // Set mock callout class 
    Test.setMock(HttpCalloutMock.class, new AnimalLocatorMock()); 
    // This causes a fake response to be sent
    // from the class that implements HttpCalloutMock. 
    String response = AnimalLocator.getAnimalById(1);
    System.assertEquals('chicken', response);
}
    }

 
nikolai calder 14nikolai calder 14
i'll leave my original question up but i finally figured it out. The problem was that because I stayed up late working on this, my brain decided not to notice the correct naming of the method is getAnimalNameById and not getAnimalById XD. Feel free to use and improve upon my original post.
Joanne VanNoord 8Joanne VanNoord 8

Hopefully this info helps other folks! 

I had tried out all the code snippets from this post, but was still getting the following -

Error :

Challenge not yet complete
Executing the 'getAnimalNameById' method on 'AnimalLocator' failed. Make sure the method exists with the name 'getAnimalNameById', is public and static, accepts an Integer and returns a String.

After checking the logs I noted the error:

10:37:10:010 FATAL_ERROR System.CalloutException: Unauthorized endpoint, please check Setup->Security->Remote site settings. endpoint = https://th-apex-http-callout.herokuapp.com/animals/99

Adding the Remote Site 'https://th-apex-http-callout.herokuapp.com' resolved my error and I was able to pass the challenge. 

Abhay Modani 3Abhay Modani 3
Hi Joanne VanNoord 8...
You have resolved my issue...
I was figuring out this issue from last 3-4 hr and getting same error continously...
Thankyou so much!!
:)
Abinesh C.PAbinesh C.P
can you post the full code for 'apex integration services apex rest callout ' @Abhay Modanl 3
Hemu KHemu K

Executing the 'getAnimalNameById' method on 'AnimalLocator' failed. Make sure the method exists with the name 'getAnimalNameById', is public and static, accepts an Integer and returns a String.
Hemu KHemu K
this is my problem can anyone help me
 
Sampreet Koshta 9Sampreet Koshta 9
Best answer by @Joanne VanNoord 8 ,if you dont have the remote site added in security ....no code will eliminate the error "Executing the 'getAnimalNameById' method on 'AnimalLocator' failed. Make sure the method exists with the name 'getAnimalNameById', is public and static, accepts an Integer and returns a String." .
Muthu kumar 53Muthu kumar 53

Follow these steps , Before writing code

=================

From Setup, enter Remote Site Settings in the Quick Find box, then click Remote Site Settings.
Click New Remote Site.
For the remote site name, enter animals_http.
For the remote site URL, enter https://th-apex-http-callout.herokuapp.com. This URL authorizes all subfolders for the endpoint, like https://th-apex-http-callout.herokuapp.com/path1 and https://th-apex-http-callout.herokuapp.com/path2.
For the description, enter Trailhead animal service: HTTP.
Click Save & New.
For the second remote site name, enter animals_soap.
For the remote site URL, enter https://th-apex-soap-service.herokuapp.com.
For the description, enter Trailhead animal service: SOAP.
Click Save.

Dmitry ZorinDmitry Zorin

This approach in AnimalLocator does the trick for me
 // parse the JSON response
        if (response.getStatusCode() == 200) {
            Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
            System.debug('>>>>>'+response.getBody());
            animals = result.get('animal');
            System.debug(animals);
        }
        if (animals != NULL  ) {
            Map<String, Object> result1 = (Map<String, Object>) JSON.deserializeUntyped(JSON.serialize(animals));
            returnValue =(String) result1.get('name');
        }
        return returnValue;
    } 

 

However, this approach didn't lead to the accomplished challenge. 

if (response.getStatusCode() == 200) {
Map<String, Object> r = (Map<String, Object>) JSON.deserializeUntyped(response.getbody());
Map<String, Object> animal = (Map<String, Object>)r.get('animal');
animalName = string.valueof(animal.get('name'));
}
return animalName;

 

Mock:
...
response.setBody('{"animal": {"id":0, "name": "majestic badger"}}');
...
 

ani ghoani gho
I received the below error
"Challenge not yet complete in myForsePlayground
Executing the 'getAnimalNameById' method on 'AnimalLocator' failed. Make sure the method exists with the name 'getAnimalNameById', is public and static, accepts an Integer and returns a String."

Even though my code was all good. 100% coverage and bla....and the bloody treason : was ...coz

I did not have the Remote Site Settings ...which i dont think was in the challenge steps ......navigate to setup search "Remote Site Settings"
enter follwing
Remote site name:  calloout
Remote site url   : https://th-apex-http-callout.herokuapp.com  you are all set to go..

The code  which was as below but without the above setting the above error...
@isTest
global class AnimalLocatorMock  implements HttpCalloutMock{
    
    // Implement this interface method
    global HTTPResponse respond(HTTPRequest request) {
        // Create a fake response
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"animal":{"id":1,"name":"chicken","eats":"chicken food","says":"cluck cluck"}}');
        response.setStatusCode(200);
        return response; 
        
    }
}

@isTest
public class AnimalLocatorTest {
    
    @isTest static  void testGetCallout() {
           Test.setMock(HttpCalloutMock.class, new AnimalLocatorMock()); 
        // Call method to test
        String result = AnimalLocator.getAnimalNameById(3);  
         string expectedResult='chicken';
        System.assertEquals(result, expectedResult);
        
        // System.assert(String.contains(result));        
    }   
    
}

public class AnimalLocator {
    
    public static String getAnimalNameById(Integer id){       
        
        String animal=null;
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/'+id);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        // If the request is successful, parse the JSON response.
        if (response.getStatusCode() == 200) {
            // Deserializes the JSON string into collections of primitive data types.
            System.debug('Received the following response :' + response.getBody());
            Map<String, Object> results = (Map<String, Object>)JSON.deserializeUntyped(response.getBody());
            Map<string,object> animals = (map<string,object>) results.get('animal');
               animal = string.valueof(animals.get('name'));
            // Cast the values in the 'animals' key as a list
            
        } 
        System.debug('Received the following animal:' + animal);
        return animal;
    }
}

 
Gabriel TropeaGabriel Tropea
I had the same problem when I wanted to validate the challenge in my trailhead, I got the following error message:
Executing the 'getAnimalNameById' method on 'AnimalLocator' failed. Make sure the method exists with the name 'getAnimalNameById', is public and static, accepts an Integer and returns a String."

I read this whole post and tried all the alternatives that you put, until I found a final combination, so I'm going to post what I did to make it work and validate the trailhead challenge:

Step 1
---------

Muthu kumar 53
Follow these steps , Before writing code
=================
From Setup, enter Remote Site Settings in the Quick Find box, then click Remote Site Settings.
Click New Remote Site.
For the remote site name, enter animals_http.
For the remote site URL, enter https://th-apex-http-callout.herokuapp.com. This URL authorizes all subfolders for the endpoint, like https://th-apex-http-callout.herokuapp.com/path1 and https://th-apex-http-callout.herokuapp.com/path2.
For the description, enter Trailhead animal service: HTTP.
Click Save & New.
For the second remote site name, enter animals_soap.
For the remote site URL, enter https://th-apex-soap-service.herokuapp.com.
For the description, enter Trailhead animal service: SOAP.
Click Save.

Step 2
---------

Write the classes and methods as detailed below, inside the Developer console and in 3 different classes: 
first class
public class AnimalLocator {
     public static String getAnimalNameById(Integer id){
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/'+id);
        request.setMethod('GET');
        System.debug('>>>>>>>'+id);
        HttpResponse response = http.send(request);
       Object animals; 
        String returnValue; 
        
        // parse the JSON response
        if (response.getStatusCode() == 200) {
            Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
            System.debug('>>>>>'+response.getBody());
            animals = result.get('animal');
            System.debug(animals);
        }
        if (animals != NULL  ) {
            Map<String, Object> result1 = (Map<String, Object>) JSON.deserializeUntyped(JSON.serialize(animals));
            returnValue =(String) result1.get('name');
        }
        return returnValue;
    } 
}

Second Class
@isTest
global class AnimalLocatorTest {
@isTest static void AnimalLocatorMock1() {
        Test.setMock(HttpCalloutMock.class, new AnimalLocatorMock());
        string result = AnimalLocator.getAnimalNameById(3);
        String expectedResult = 'chicken';
        //System.assertEquals(result,expectedResult );
    }
}

Third class
@isTest
global class AnimalLocatorMock implements HttpCalloutMock{
     global HTTPResponse respond(HTTPRequest request) {
        // Create a fake response
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"animal": {"id":99,"name":"trailhead","eats":"burritos","says":"more badgers"}}');
        response.setStatusCode(200);
        return response; 
    }
}

After creating the 3 classes, do "save all" in the console menu, then in the "Test" menu, select the "Run All" option, you should have no errors.
Go back to the trailhead screen to validate the challenge, first refresh the screen and then press the validate button, you should earn the 500 points.
I hope this step by step works for you as it did for me.
I want to thank all those who posted their doubts and successes in this page, since it has been a great help to overcome the challenge.
Greetings and good luck. 
Tushar Thakare 7Tushar Thakare 7
For those who are still facing the problem, try below solution
  1. Add the url in remote site setting 
If it solved your issue, please mark it as good answer.
Thanks
chetan shetty 10chetan shetty 10
Thanks Gabriel, that worked like a charm!
Akansha Singh 28Akansha Singh 28
Hi , anyone who is facing such problem my devsole guided me by its own check your logs and see where you're getting exception.
Go to setup>Remote Site Settings>New Remote site > name of point  ->>>> endpoint: url ->>>>>>https://th-apex-http-callout.herokuapp.com


Thanks
rio conwayrio conway
If you open the developer console and run the following what do you get back? (https://zucchero.com.au/trolls-birthday-cake/)
Nushi Davoud 16Nushi Davoud 16
Challenge not yet complete in Build Travel App
Executing the 'getAnimalNameById' method on 'AnimalLocator' failed. Make sure the method exists with the name 'getAnimalNameById', is public and static, accepts an Integer, and returns a String.
I'm still getting same error @Gabriel Tropea
Arkon PradhanArkon Pradhan
Greetings everyone, and thank you ani gho, used your solution and it worked. Though I am wondering why my solution did not work, can anybody help me out with it? My tests failed and gave this error: System.TypeException: Invalid conversion from runtime type List&lt;ANY&gt; to Map&lt;String,ANY&gt;. Also I had got only 83% code coverage, with the last two lines not being covered, can someone please help me figure this out.
public class AnimalLocator {
    public static String getAnimalNameById(Integer id) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/'+id);
        request.setMethod('GET');
        
        HttpResponse response = http.send(request);
        String animalName = '';	
        if(response.getStatusCode() == 200) {
            Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
            Map<string, Object> animal = (Map<String, Object>) result.get('animal');
            animalName = String.valueOf(animal.get('name'));
         }
        return animalName;
    }
}

// MockResponseClass
@isTest
global class AnimalLocatorMock implements HttpCalloutMock {
    // Implement this interface method
    global HTTPResponse respond(HTTPRequest request) {
        // Create a fake response
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"animal":[{"id":1,"name":"chicken","eats":"chicken food","says":"cluck cluck"}]}');
        response.setStatusCode(200);
        return response; 
    }
}

// Test Class
@isTest
private class AnimalsLocatorTest {
    @isTest static void testGetCallout() {
        Test.setMock(HttpCalloutMock.class, new AnimalLocatorMock()); 
        
        String name = AnimalLocator.getAnimalNameById(1);
        System.assertEquals('chicken', name);
    }  
}