0

this is my first time trying to create an apex class and deploy it. It is very basic and is just the back end for a visualforce page. However to deploy to production I need to make a test apex class which I do not know how to do.

Here is the apex class for the visualforce page:

public class MarketingContactController {
    //variables used in code that may be displayed in the page
    public Decimal countMarketingContacts {get;set;}
    public Decimal marketingContactsPercent;
    public Decimal roundedContactsPercent {get;set;}
    public Decimal availableMarketingContacts {get;set;}
    public List <Contact> marketingContacts;

    public MarketingContactController(){
        //Get marketing contacts
        List <Contact> marketingContacts = new list <Contact> ([select id from contact where marketing_contact__c = TRUE WITH SECURITY_ENFORCED]);
        //Set countMarketingContacts variable to be the number of marketing contacts returned from the SOQL Query
        countMarketingContacts = marketingContacts.size();
        //Set availableMarketingContacts variable for the visualforce page and to do percentage calculation
        availableMarketingContacts = 20000;
        //Work out the (number of marketing contacts / available contacts) and then make it a percentage
        marketingContactsPercent = ((countMarketingContacts/availableMarketingContacts)*100);
        //Round the number of marketing contacts to 2 decimal places
        roundedContactsPercent = marketingContactsPercent.setScale(2);
        }
    }

and here is the small test class I have tried to make:

@isTest
public class marketingContactControllerTest {
    @isTest static void testPositiveCase() {
    decimal cMc = MarketingContactController.countMarketingContacts(3.00);
    decimal aMc = MarketingContactController.availableMarketingContacts(20000.00);
    decimal rCp = MarketingContactController.roundedContactsPercent();
    system.assertEquals(0.01500,rCp);
    }
}

I have 3 errors that all say: Method does not exist or incorrect signature: void countMarketingContacts(Decimal) from the type MarketingContactController and then the same for the other variables.

1 Answer 1

0

In a nutshell, unit testing is just about calling your code so that it runs. It's usually said that there are 3 parts to a unit test: AAA

  • Arrange
    • This is setting up the test data, and all of the other bits and bobs that the class you're testing needs to be able to function. Apex unit tests are isolated from the "real" data, so you need to explicitly create things like SObject records (i.e. at least one Contact so that your query returns data)
  • Act
    • Basically, call the method you want to test (and each test should only really stress a single method). Since you only have a constructor here, that'd be something like MarketingContactController testController = new MarketingContactController();
  • Assert
    • Salesforce requires 75% coverage, but the real value of unit tests for us (as developers) are in the assertions. Put simply, you want to gather the output from running the method in the class being tested and make sure that it did what you expected it to. If add2ToNumber(2) doesn't result in 4, then something isn't right

The 3 errors you're getting on these lines

decimal cMc = MarketingContactController.countMarketingContacts(3.00);
decimal aMc = MarketingContactController.availableMarketingContacts(20000.00);
decimal rCp = MarketingContactController.roundedContactsPercent();

are because countMarketingContacts, availableMarketingContacts, and roundedContactsPercent are instance variables (well... properties, but that's splitting hairs), but you're trying to use them as if they were static methods.

Being instance variables, you need an instance of your controller to access them, and you wouldn't use parenthesis. E.g.

MarketingContactController testControllerInstance = new MarketingContactController();

testControllerInstance.countMarketingContacts = 3.0;
testControllerInstance.availableMarketingContacts = 20000.0;

That's all well and good, but the above isn't really useful in a test. You can't set instance variables without having an instance, and you can't test the constructor if you can't provide the input that it needs.

The conclusion here is that your test should not try to set those 2 instance variables directly. You need to create at least one Contact for your test so that the query in your constructor can find it and set countMarketingContacts for you.

@isTest
public class marketingContactControllerTest {
    // Best practice is to use an @TestSetup annotated method to do, well...
    //   test setup.
    // This covers things like creating/inserting SObject records
    @TestSetup
    static void makeData(){
        // The startTest()/stopTest() calls here get you a fresh set of
        //   governor limits.
        // It's not so important right now, but for tests with a lot of setup
        //   it'll help you avoid governor limits.
        // Without this, all of the cpu/queries/dml/etc... you do here will be
        //   counted against you in every test method
        Test.startTest();

        // We can use the SObject constructor to specify name-value pairs.
        // This only works with SObjects (like Contact).
        // Can save a bit of typing
        Contact testContact1 = new Contact(
            LastName = 'McTest'
            Marketing_Contact__c = true
        );

        insert new List<Contact>{testContact1};
        Test.stopTest();
    }

    // Personal preference, have the annotation on its own line.
    // Makes it easy to disable a test method (if you ever need to do that)
    //   because you only need to comment out the annotation instead of the
    //   entire method.
    @isTest 
    static void testPositiveCase() {
        // Getting a fresh set of limits again.
        // Again, not that important here, but it is good practice
        Test.startTest();
        MarketingContactController testControllerInstance = new MarketingContactController();
        Test.stopTest();
    }
}

So that covers the "Arrange" and "Act" bits. What about "Assert"?

Like I said before, the heart of assertions is gathering output/results and comparing them against your expectations. The output of calling a method can be:

  • the value it returns
  • checking to see if it inserted, updated, or deleted an SObject record
  • the value contained in instance variables (or static variables)
  • the amount of a particular governor limit that was used

In your particular case, it's checking the value of roundedContactsPercent that you want to do.

Since it's an instance variable, you access it with dot notation.

// prior lines omitted
Test.stopTest();

// Using the new-ish System.Assert class, which has methods that are a bit more
//   intuitive
// The first argument is the expected value
// The second argument is the actual value
// The third argument is optional, but it is best practice to include it.
// It's a message that will be included if the assertion fails, and that makes
//   it a lot easier to understand and pinpoint issues.
System.Assert.areEqual(1.0 / testControllerInstance.countMarketingContacts, testControllerInstance.roundedContactsPercent, 'The contacts percentage is wrong');

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .