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');