+ Start a Discussion
Kamil MieczakowskiKamil Mieczakowski 

for loop test

Hi, I am attempting to write a simple test for a for loop that cleans the websites fields of unwanted elements such as 'http', 'www' etc. The script works for me but the test class that I wrote gets me 0% of coverage. Could anyone advise on what I am doing wrong? Thank you in advance.

Here's the actual script:
 
public class standardiseWebsites {
    list<Account> accts = [SELECT Website FROM Account];
    public standardiseWebsites(){
   
        for (Account acct : accts){
            string website = acct.website; 
        	website = website.replace('http://www.','');
            website = website.replace('https://www.','');
            website = website.replace('https://','');
            website = website.replace('http://','');
            website = website.replace('www.','');
            acct.website = website;
            update acct;
            
        }
    }
}

And here's the test script:
 
@isTest
public class standardiseWebsitesTest {
    static testMethod void standardiseWebsitesTest(){
        //Create test Accounts with various types of websites
        
        Account account1 = new Account(Name = 'Test Account1', Website='https://www.test.com');
        insert account1;
		Account account2 = new Account(Name = 'Test Account1', Website='http://www.test.com');
		insert account2;
		Account account3 = new Account(Name = 'Test Account1', Website='https://test.com');
		insert account3;                                      
		Account account4 = new Account(Name = 'Test Account1', Website='http://test.com');
		insert account4;
		Account account5 = new Account(Name = 'Test Account1', Website='www.test.com');                                                           
        insert account5;
		Account account6 = new Account(Name = 'Test Account1', Website='test.com');                                                           
        insert account6;

        Test.startTest();
        
        System.debug(accts);
		StandardiseWebsitesTest obj = new standardiseWebsitesTest();
		
        Test.stopTest();
        
        //Check the desired results
        system.assertEquals(Account1.Website,'test.com');
        system.assertEquals(Account2.Website,'test.com');            
        system.assertEquals(Account3.Website,'test.com');            
        system.assertEquals(Account4.Website,'test.com');
        system.assertEquals(Account5.Website,'test.com');            
        system.assertEquals(Account6.Website,'test.com');        
        
        
        
    }

}

 
Best Answer chosen by Kamil Mieczakowski
Charisse de BelenCharisse de Belen
Hi Kamil,

Just as a quick tip, many other programming languages will not allow you to make your method name the same as the class name (if you understand what a constructor is, then you may have an idea of why), so I changed your method name to simply standardise() to make things less confusing. Anyway, here are some other suggestions:
  • Don't forget to use the @isTest or testMethod attribute on your test method.
  • In order for you to call your standardise() method the way you are calling it in your test, you need to make it static (http://www.sfdc99.com/2015/01/22/introduction-to-the-static-keyword/).
  • The reason you are not getting full coverage is because test classes do not access existing records. In other words, your query in your standardiseWebsites class will not return any records when you run it in your test. You can fix this by adding some test data in your test class.
  • Last but not least, make sure that the results of the standardisation are correct by adding some assertions.

standardiseWebsites:
public class standardiseWebsites {
 
    public static void standardise(){
    	list<Account> accts = [SELECT Website FROM Account WHERE Website LIKE 'http://%' OR Website LIKE 'https://%'];
        for (Account acct : accts){            
            string website = acct.Website;
            Url u = new Url(acct.Website);
            acct.Website = u.getHost();
            update acct;
        }
        
    	list<Account> accts2 = [SELECT Website FROM Account WHERE Website LIKE 'www.%'];
        for (Account acct2 : accts2){
            string website = acct2.website;
            website = website.replace('www.','');
            acct2.website = website;
            update acct2;
        }
    }
    
}

standardiseWebsitesTest:
@isTest
public class standardiseWebsitesTest {
 
    @isTest
    static void standardiseWebsitesTest() {
        //Create test Accounts with various types of websites
		List<Account> accts = new List<Account>();
        
        Account account1 = new Account(Name = 'Test Account1', Website='https://www.test.com');
        accts.add(account1);
		Account account2 = new Account(Name = 'Test Account1', Website='http://www.test.com');
		accts.add(account2);
		Account account3 = new Account(Name = 'Test Account1', Website='https://test.com');
		accts.add(account3);                                    
		Account account4 = new Account(Name = 'Test Account1', Website='http://test.com');
		accts.add(account4);
		Account account5 = new Account(Name = 'Test Account1', Website='www.test.com');
        accts.add(account5);
		Account account6 = new Account(Name = 'Test Account1', Website='test.com');
        accts.add(account6);
        
        insert accts;
        
        // Standardise the websites
        standardiseWebsites.standardise();
        
        // Check the desired results
        accts = [SELECT Website FROM Account];
        for (Account acct : accts) {
            System.assertEquals('test.com', acct.Website);
        }
    }
    
}

​I hope this helps!

All Answers

Charisse de BelenCharisse de Belen
Hi Kamil,

There are a few different things that are wrong with your code:
  • The definition of the standardiseWebsites() method needs a return type. You probably want to make it void:
public void standardiseWebsites(){
    ...
}
  • In line 22 of your test class, you are trying to call the test class inside of itself. I think you want to do this instead:
standardiseWebsites obj = new standardiseWebsites();
obj.standardiseWebsites();
  • You do not need line 19 or 24 of your test class. These are useful if you need to run asynchronous test code synchronously, or if you are close to exceeding the governor limits, but you are doing neither of these things in your code.
  • In your test class, you are trying to assert that AccountX.Website is equal to 'test.com'. This will not work because AccountX is simply holding the value you initially put in it. The variable does not directly reference the actual Account records, so the value in the variable will not change after you call your standardiseWebsites() method. In order to check each updated record, you will need to perform a SOQL query to get all the account websites after you call the standardiseWebsites() method. You can then write a for loop to iterate through all the accounts and assert that they are correct.

Once you fix all of these, please feel free to post your updated code here and I will be happy to look through it again and make sure it looks correct. (I also have some best practice tips I would like to share after you get your code working.)
Kamil MieczakowskiKamil Mieczakowski
Hi Charisse, Thank you for your reply and sharing valuable insights . I played around with this script a little bit. The first thing that I decided to fix was the logic itself. The way I initially wanted to convert whatever was in the website field into pure host address (with no 'http', 'www' etc) was risky and I could potentially damage some addresses by doing so.

Therefore I decided to extract the host using the getHost() method in Apex:
Url u = new Url(acct.Website);
acct.Website = u.getHost()+u.getPath();
However ,I am now facing another problem. Some of the Website addresses are already recorded without the protocol (without 'http(s)://' or www. preceding it, e g google.com instead of https://google.com), and when the script comes across them it throws the below error:
System.StringException: no protocol: google.com
So I have a couple of questions:

1. How do I create an if loop inside of a for loop that would determine if the given website address comes with a protocol. If it does it would run the script and if it doesn't it would move on to the next website. I tried to use the following condition, but it would return 'Method does not exist or incorrect signature: [Url].Contains(String)' error:
if (u.Contains('http%') || u.Contains('https%') || u.Contains('www.%')){}
2. Why is my script only running when I don't define the method as void ? As soon as I add void to the method the script runs but with no effect.

Here's the script (I marked the relevant if loop with question marks):
 
public class standardiseWebsites {
    
    public standardiseWebsites(){
        
        list<Account> accts = [SELECT Website FROM Account WHERE Website <> ''];
        
        for (Account acct : accts){
        string website = acct.website;
        Url u = new Url(acct.Website);
 
            if (??????????) {         
            acct.Website = u.getHost();
            acct.website = website;
            update acct;  
        }
        }
    }
}

3. If I now add an if loop into the script how does it impact testing?

Thank you in advance for all your answers. 
Kamil MieczakowskiKamil Mieczakowski
Update to the above. This is the newest version of my script, but I have no idea how to test it. 
 
public class standardiseWebsites {
 
    public void standardiseWebsites(){
    list<Account> accts = [SELECT Website FROM Account WHERE Website LIKE 'http://%' OR Website LIKE 'https://%'];
        for (Account acct : accts){            
            string website = acct.Website;
            Url u = new Url(acct.Website);
            acct.Website = u.getHost();
            update acct;
        }
        
    list<Account> accts2 = [SELECT Website FROM Account WHERE Website LIKE 'www.%'];
        for (Account acct2 : accts2){
            string website = acct2.website;
            website = website.replace('www.','');
            acct2.website = website;
            update acct2;
        }
    }
}

 
Kamil MieczakowskiKamil Mieczakowski
And here is the most recent version of my test. I do not understand why I am only geting 38% coverage:
 
@isTest
public class standardiseWebsitesTest {
 
        static void standardiseWebsitesTest() {
        standardiseWebsites.standardiseWebsites();
    }
}

 
Charisse de BelenCharisse de Belen
Hi Kamil,

Just as a quick tip, many other programming languages will not allow you to make your method name the same as the class name (if you understand what a constructor is, then you may have an idea of why), so I changed your method name to simply standardise() to make things less confusing. Anyway, here are some other suggestions:
  • Don't forget to use the @isTest or testMethod attribute on your test method.
  • In order for you to call your standardise() method the way you are calling it in your test, you need to make it static (http://www.sfdc99.com/2015/01/22/introduction-to-the-static-keyword/).
  • The reason you are not getting full coverage is because test classes do not access existing records. In other words, your query in your standardiseWebsites class will not return any records when you run it in your test. You can fix this by adding some test data in your test class.
  • Last but not least, make sure that the results of the standardisation are correct by adding some assertions.

standardiseWebsites:
public class standardiseWebsites {
 
    public static void standardise(){
    	list<Account> accts = [SELECT Website FROM Account WHERE Website LIKE 'http://%' OR Website LIKE 'https://%'];
        for (Account acct : accts){            
            string website = acct.Website;
            Url u = new Url(acct.Website);
            acct.Website = u.getHost();
            update acct;
        }
        
    	list<Account> accts2 = [SELECT Website FROM Account WHERE Website LIKE 'www.%'];
        for (Account acct2 : accts2){
            string website = acct2.website;
            website = website.replace('www.','');
            acct2.website = website;
            update acct2;
        }
    }
    
}

standardiseWebsitesTest:
@isTest
public class standardiseWebsitesTest {
 
    @isTest
    static void standardiseWebsitesTest() {
        //Create test Accounts with various types of websites
		List<Account> accts = new List<Account>();
        
        Account account1 = new Account(Name = 'Test Account1', Website='https://www.test.com');
        accts.add(account1);
		Account account2 = new Account(Name = 'Test Account1', Website='http://www.test.com');
		accts.add(account2);
		Account account3 = new Account(Name = 'Test Account1', Website='https://test.com');
		accts.add(account3);                                    
		Account account4 = new Account(Name = 'Test Account1', Website='http://test.com');
		accts.add(account4);
		Account account5 = new Account(Name = 'Test Account1', Website='www.test.com');
        accts.add(account5);
		Account account6 = new Account(Name = 'Test Account1', Website='test.com');
        accts.add(account6);
        
        insert accts;
        
        // Standardise the websites
        standardiseWebsites.standardise();
        
        // Check the desired results
        accts = [SELECT Website FROM Account];
        for (Account acct : accts) {
            System.assertEquals('test.com', acct.Website);
        }
    }
    
}

​I hope this helps!
This was selected as the best answer