-2

Apex class :-

global class BlockIEPolicyCondition implements TxnSecurity.PolicyCondition {
    public boolean evaluate(TxnSecurity.Event e) {
        List<User> users = [SELECT ProfileId FROM User WHERE Id = :e.UserId];
        String profileId = (String)users.get(0).get('ProfileId');
        List<Profile> profiles = [SELECT Name FROM Profile WHERE Id = :profileId];
        String profileName = (String)profiles.get(0).get('Name');
        LoginHistory eObj = [SELECT Browser FROM LoginHistory WHERE Id = :e.data.get('LoginHistoryId')];
        String profileName1 = '"' +profileName + '"';
        String P=system.label.BlockIEPolicyProfile;
        Boolean ismatch = p.contains(profileName1);
        if(ismatch){
            if(eObj.Browser == 'IE 11' || eObj.Browser == 'IE 10' || eObj.Browser == 'IE 9' || eObj.Browser == 'IE 8') {
                return true;
            }
        }

        return false; 
    }
}

Apex test class :-

@isTest
public class BlockIEPolicyCondition_Test {
    static list < User > userList = new list < User > ();

    public static void setupTestData() {
        User use1 = SRVS_CreateTestDataFactory.createUser('Service Integration Profile');
        use1.FederationIdentifier = '9098';
        insert use1;

        User use2 = SRVS_CreateTestDataFactory.createUser('System Read Only');
        use2.FederationIdentifier = '4567';
        insert use2;

        userList.add(use2);
        userList.add(use1);
    }

    @isTest
    public static void testIsNotIE() {
        User use3 = SRVS_CreateTestDataFactory.createUser('System Administrator');
        use3.FederationIdentifier = '9876';
        insert use3;

        system.runAs(use3){
            Test.startTest();
            List < Loginhistory > event = [select LoginTime, Browser from loginhistory];
            BlockIEPolicyCondition condition = new BlockIEPolicyCondition();
            Test.stopTest();
        }

        System.assertEquals(0, userList.size());
    }
}
1
  • 3
    It would be better if you share in which lines you are not able to cover. Jun 4, 2018 at 10:04

1 Answer 1

5

Questions on code coverage are very common here, and they are commonly closed because the general advice is the same every time.

I myself have some words that I repeat quite often regarding unit testing.

The golden rule of unit testing

In unit testing, you only gain coverage for code that is executed as part of a test method. It really is that simple.

You are completely in control of your unit test; so, if you're not getting coverage for part of your code, and want coverage for it, you need to ask yourself "how can I cause this code to be executed?"

  • If you're not gaining coverage for a method, then you need to have a test that actually calls the method.
  • If you're not gaining coverage for an else block, then you need to have a test where you ensure that you do not satisfy the conditions to enter the corresponding if block.
  • If you're not gaining coverage for a loop, you need to make sure that your test actually has records to loop over.

Most times, fixing coverage issues comes down to making sure that you create/insert appropriate test data (ideally, in a method with the @testSetup annotation).

The most important part of unit testing

Coverage is what Salesforce requires, but code coverage alone is pretty meaningless.

The way I see things, the real use of unit tests is both to verify that your code behaves the way you think it should, and produces the correct output, as well as acting as a guard to alert you when changes or additions you make in the future break existing code.

For both of those purposes, you need to use assertions. Without assertions, you're only checking to make sure that your code doesn't crash/fail catastrophically. Your code could, for example, tell you that 2 + 2 = banana (joking side note: everyone knows that 2 + 2 = fish), or that red is equal to blue, you don't know unless you make assertions.

What to assert in unit tests?

The answer here is the always helpful "it depends". You could write an assertion for almost everything under the sun, but it takes time to write assertions (and to write code that satisfies them). After a while, you'll learn what things are important to assert, and which things don't need assertions.

Personally, the things I find myself writing assertions for most frequently are:

  • Initial values - When I create a new instance of a class, are the class variables initialized to something useful?
  • Return values - When a method finishes and returns, did I get the value that I expected?
  • Changes to object state - If I perform some action (most often, calling a method that does not return any data), does the object instance that I performed the action on have its internal state modified? Was the modification what I expected?
  • Changes to the database - If I call a method that's supposed to insert an Opportunity, was a new Opportunity really inserted?
  • Consumption of governor limits - This one is less frequent, and starts to matter when you have triggers that cause other triggers to fire. What I assert here is that my code only uses a certain number of queries, or that it consumes less than X amount of the CPU governor limit

You can (and likely should) have more than one unit test

A unit test is supposed to be just that, a test of a single "unit" of code. The definition of a "unit" of code can vary from person to person, and situation to situation, but for me it's usually a single method of the class I want to test.

Trying to cram everything into a single unit test makes the test setup harder, and makes it more complicated to actually test things (and also harder to debug after a test fails).

Beyond breaking up tests to be per-method, I like to further break up tests to test specific, unique conditions.

I'll have one or two tests to see what happens on the "happy path", where all required input is present and well-formed.

I'll have several tests to see what happens when a null input is given, or if a record I expect to be able to query doesn't exist, or if someone who doesn't have permission to perform an action tries to perform that action.

These additional tests help to ensure that code is robust (i.e. able to gracefully handle varied and less than ideal conditions).

A nice thing about writing a variety of tests is that if you have unit tests to cover a variety of situations, you can largely stop worrying about getting enough code coverage. If you test your code against enough different situations, you will naturally gain coverage.

Your specific case (finally)

Code that relies on standard Salesforce history objects are hard to test, because we don't have the ability to insert history records explicitly, and inserting/updating normal records (like an Account) does not cause a history record to be generated when executing a unit test.

In "Is it possible to test apex that relies on field history tracking?", I explain that you can use Test.loadData() to create otherwise uncreate-able data. The big caveat is that if you try to query the resulting records, you may not get back some (or any) of the data.

To gain the coverage you're looking for, you'll need to call your class's execute(TxnSecurity.Event) method, and you'll need to set the loginHistoryId of TxnSecurity.Event.data.

I think the general flow of your test will look something like

  1. Call Test.loadData(), store the result in a list/map
  2. Instantiate a TxnSecurity.Event object
  3. e.data.put('loginHistoryId',`)
  4. Create an instance of the class you're trying to test
  5. Call the execute() method, and pass in your object from step 2
  6. Assert that the response is true/false (depending on which one of your, at least 2, test methods you are in)

You'll need to experiment with whether the queried results from what you inserted via Test.loadData() actually contains the field(s) you need. If that doesn't work, then I'd try writing some test-specific code to inject the data you need, something like...

global class BlockIEPolicyCondition implements TxnSecurity.PolicyCondition {
    // The private modifier means that nothing else outside of this class can
    //   see/modify/use the variable
    // The @testVisible annotation makes an exception to the above, allowing
    //   a test class to get/set the value
    @testVisible
    private LoginHistory testHistory;

    public boolean evaluate(TxnSecurity.Event e) {
        //<existing code unchanged and omitted>

        // We still want to run this query so we get coverage
        LoginHistory eObj = [SELECT Browser FROM LoginHistory WHERE Id = :e.data.get('LoginHistoryId')];

        // ...but in a test, we want to use our specific record
        if(Test.isRunningTest()){
            eObj = this.testHistory;
        }

        //<remaining code is unchanged, and omitted>
    }
}

I hope that's enough to put you on the right path.

You must log in to answer this question.

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