Monday, July 8, 2013

Apex Compile Error: Test methods must be in test classes

         Apex Compile Error: Test methods must be in test classes


            Have you ever got an error "Error: Compile Error: Test methods must be in test classes at line 37 column 28" while saving an Apex Class? and wondered why is it an error? It used to work without any error & now, it is not working! Nothing to worry, this is an update starting from Summer '13 release. Here is an excerpt from the release notes:

Test Methods Defined in Test Classes:


Starting in Summer ‘13, test methods can be defined only in test classes (classes annotated with @isTest). You can no longer add a test method in a non-test class. This change applies to new Apex code saved using Salesforce.com API version 28.0 and later. Apex code saved using earlier API versions isn’t affected. 

This change is in line with the Apex coding best practice of including test methods in test classes. Test classes don’t count toward your organization’s code size limit, so separating the test code from the product code enables you to write more Apex code. Also, Apex testing will be enhanced in future releases and this is a prerequisite for those planned enhancements.

Lets take an example TestController class (Prior to Salesforce.com API Version 28.0) it was allowed to have the test methods also in the same class as shown bellow. 


//This class will give Compilation error "Test methods must be in test classes at line 25 column 28"  
public class TestController { //Declarations private Integer inventoryCount = 100; // Instance method public String DisplayStatus() { String status = 'Current inventory status: ' + inventoryCount + ' records.'; System.debug(LoggingLevel.INFO, status); return status; } // Static method public static Id InsertAccount(String aName, String bCity) { Account a = new Account(Name=aName, BillingCity=bCity); insert a; return a.Id; } /* *Unit test methods **/ static testmethod void testDisplayStatus() { // To call the instance method, you must first create an instance of the class. TestController m = new TestController(); String s = m.DisplayStatus(); // Verify status message System.assertNotEquals(null, s); } static testmethod void testInsertRecord() { // Call the static method Id accountID = TestController.InsertRecord('Acme','New York'); // Verify record got inserted System.assert(accountID != null); Account a = [SELECT Name,BillingCity FROM Account WHERE Id=:accountID]; System.assertEquals('Acme',a.Name); System.assertEquals('New York',a.BillingCity); } }

We can resolve this compilation error by moving all the test methods to new Test class "TestContollerTest.cls"

1. Create a new (test) class & name it "TestContollerTest". This class should have @isTest annotation to indicate this is a test class
2. Remove all methods from the Original class and add (Cut & Paste) them in the new Test class. (Note: if you have used keywords like "this" to reference the static variables and/or methods you should remove those references. For e.g.: this.<methodName> to <className>.<methodName> etc.)

The following example shows how to add test methods in a separate test class, TestControllerTest for the main class TestController.

public class TestController { //Declarations private Integer inventoryCount = 100; // Instance method public String DisplayStatus() { String status = 'Current inventory status: ' + inventoryCount + ' records.'; System.debug(LoggingLevel.INFO, status); return status; } // Static method public static Id InsertAccount(String aName, String bCity) { Account a = new Account(Name=aName, BillingCity=bCity); insert a; return a.Id; } }

@isTest private class TestControllerTest { static testmethod void testDisplayStatus() { // To call the instance method, you must first create an instance of the class. TestController m = new TestController(); String s = m.DisplayStatus(); // Verify status message System.assertNotEquals(null, s); } static testmethod void testInsertAccount() { // Call the static method Id accountID = TestController.InsertAccount('Acme','New York'); // Verify record got inserted System.assert(accountID != null); Account a = [SELECT Name,BillingCity FROM Account WHERE Id=:accountID]; System.assertEquals('Acme',a.Name); System.assertEquals('New York',a.BillingCity); } }