[SalesForce] How to force test class to run batch only once

I am writing a test class for batch class. but it is throwing error:

System.LimitException: Too many async jobs enqueued for this apex test
context Stack Trace External entry point

I am calling batch class from finish method again, but i dont know how to stop calling it again in test class. I have set body in mock class that is returning just one product. Please guide me how can i make it call once.

public class ShopifyFetchProductsBatch implements Database.Batchable<ShopifyAPI.ShopifyProduct>, Database.AllowsCallouts, Database.Stateful 
{
    public integer pageNumber;
    public boolean doMore;
    public ShopifyFetchProductsBatch(integer pNumber)
    {
        pageNumber = pNumber;
        doMore = true;
    }

    public Iterable<ShopifyAPI.ShopifyProduct> start(Database.BatchableContext BC)
    {
        system.debug('Start:' + pageNumber);
        List<ShopifyAPI.ShopifyProduct> prods = ShopifyApi.GetAllProducts(50,pageNumber).products;
        doMore = prods.size() > 0;
        return prods;
    }   

    public void execute (Database.BatchableContext BC, List<ShopifyAPI.ShopifyProduct> prods)
    {
        system.debug('Execute:' + (prods.size()));
        List<Shopify_Product__c> sProds = new List<Shopify_Product__c>();
        List<Shopify_Product_Variant__c> sVars = new List<Shopify_Product_Variant__c>();
        for(ShopifyAPI.ShopifyProduct p : prods)
        {
            Shopify_Product__c sp = new Shopify_Product__c(name = p.id);
            sProds.add(sp);
        }
        insert sProds;
        for(ShopifyAPI.ShopifyProduct p : prods)
        {
            for(ShopifyAPI.ShopifyVariant var : p.variants)
            {
                Shopify_Product_Variant__c v = new Shopify_Product_Variant__c();
                v.sku__c = var.sku;
                v.shopify_product_id__c = var.product_id;
                v.Name = var.id;
                for(Shopify_Product__c pro : sProds)
                {
                    if(pro.Name == v.shopify_product_id__c)
                    {
                        v.shopify_product__c = pro.ID;
                    }
                }
                sVars.add(v);
            }
        }
        insert sVars;
    }

    public void finish (Database.BatchableContext BC)
    {
        if(doMore)    
        {
            system.debug('Do More:' + (pageNumber+1));
            ShopifyFetchProductsBatch b = new ShopifyFetchProductsBatch(pageNumber+1);
            database.executeBatch(b,50);
        }
        else
        {
            system.debug('No Do More:' + (pageNumber));
            ShopifyUpdateProductsBatch b = new ShopifyUpdateProductsBatch();
            database.executeBatch(b,5);
        }
    }

}

TestClass:

@IsTest(seeAllData=true)

Public Class TestShopifyFetchProductsBatch
{   
    Static testMethod void Test()
    {               
        Test.StartTest();      
        Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
        ShopifyFetchProductsBatch b = new ShopifyFetchProductsBatch(1);
        b.start(null);
        Database.executebatch(b);
        Test.StopTest();   
    }  
}

Best Answer

It is difficult to write good tests for cases where one batchable chains to the next.

The most direct way to work around your immediate problem is to make use of the Test.isRunningTest method to turn off the chaining in the finish method when the code is run from a test:

public void finish (Database.BatchableContext BC)
{
    if (!Test.isRunningTest())
    {
        if(doMore)
        {

This is appropriate if the majority of the logic you are trying to test is in the execute method. All you can do to check the chaining functionality is to review the code with other developers and not change it in the future without further careful review.

Related Topic