Python and Simple Salesforce in Robot Framework (Tutorial 5)

 
 
 
 
 

As I continue to look for Salesforce test solutions that support test automation, I am finding there is no shortage. And even better two of my favorite tools, Python and Robot Framework, are in the mix. Though Simple Salesforce is designed for Python, that translates in my mind that it is designed for Robot Framework. The reason is that if you can create functions in Python and execute, you can call those functions as keyword functions in Robot Framework. Then you have a way of getting good test results from Robot Framework, and you can introduce a good input driver, such as Excel combined with a Data Driver library.

There is a 3000+ page PDF document entitled SOAP API Developer Guide which is a part of the Salesforce Developer Website. Though the document references SOAP in the title, it includes documentation for REST and Bulk API solutions. Here are excerpts from the document that might help with your understanding of the REST API and BULK API solutions.

REST API

REST API provides a powerful, convenient, and simple REST-based web services interface for interacting with Salesforce. Its advantages include ease of integration and development, and it’s an excellent choice of technology for use with mobile applications and web projects. For certain projects, you may want to use REST API with other Salesforce REST APIs. To build UI for creating, reading, updating, and deleting records, including building UI for list views, actions, and dependent picklists, use User Interface API. To build UI for B2B Commerce on Lightning, CMS managed content, Experience Cloud sites, or Chatter, use Connect REST API. If you have many records to process, consider using Bulk API, which is based on REST principles and optimized for large sets of data.

Bulk API

Use Bulk API 2.0 to QUERY, QUERYALL, INSERT, UPDATE, UPSERT, or DELETE many records asynchronously. Bulk API 2.0 is designed on the Salesforce REST framework. Other Salesforce Platform APIs, such as SOAP API or REST API, are optimized for synchronous client applications that update a few records at a time. You can use SOAP API for processing many records, but when the data sets contain hundreds of thousands of records, synchronous operations are less practical. Bulk API 2.0 is designed to make it simple to process data from a few thousand to millions of records.

MORE ON REST API

I intend to continue with a discussion on the REST API solution of Simple Salesforce. To interact with this solution requires Python functions that make a call to Simple Salesforce in the following format:

  • SF is the Simple Salesforce object instance
  • sObject is the name of the Salesforce object, such as, Account, Contact, Lead, Opportunity, etc.
  • operator() is the Salesforce operation, such as, Create, Update, Get, Delete, etc.

SF.sObject.operator() where

I lean toward recommending the REST API instead of SOAP API because it is where I see true simplicity. To start with, Simple Salesforce provides a simple login process after you import it into your Python program using the following statement.

from simple_salesforce import Salesforce

Python Function to perform Simple Salesforce Connection

  1. def login_to_salesforce(sfdc_user, psw, security_token):
  2.  
  3.  # * username — the Salesforce username to use for authentication
  4.  # * password — the password for the username
  5.  # * security_token — the security token for the username
  6.  # * domain — The domain to using for connecting to Salesforce. Use
  7.  #             common domains, such as ‘login’ or ‘test’, or
  8.  #             Salesforce My domain. If not used, will default to
  9.  #             ‘login’.
  10.  
  11. # Simple Salesforce Login
  12. sf = Salesforce(username=sfdc_user, password=psw, security_token=security_token)
  13. return sf

 

A connection variable, such as, “sf” can be assigned the connection data as shown on line 12 above. Notice in the comments (lines 3-9) an additional variable can be specified labelled DOMAIN. For most Salesforce instances the domain defaults to “login” which is acceptable. But when you are using a sandbox instance of Salesforce, you need to specify the DOMAIN as “test”.

Also, notice that the object “sf” is returned from the function. This object can then be used for further interaction with Salesforce to get or manipulate data in Salesforce. For example, in the lines of Python code below in figure 2 you can see several functions that use the “sf” variable as the first argument. This allows the create, update, query, and get functions to continue connectivity with Salesforce, and make legal requests for data management. The common output of these functions is the ID value which is the key to the record or records.

Use the chart that immediately follows in figure 1 to improve your understanding of the functions and their use.

Figure 1

FUNCTION INPUT OUTPUT
Get Salesforce Obj, Object, ID Data
Create Salesforce Obj, Object, Data ID, Status, Error
Update Salesforce Obj, Object, ID, Data ID, Status, Error
Query Salesforce Obj, Object, Query Table

Figure 2

   
  1. def create_salesforce_object(sf, sobject, object_data):
        try:
            o = sobject
            sobject_data = getattr(sf, o).create(object_data)
        except Exception as e:
            print(e)
            return e
        id = sobject_data[‘id’]
        if sobject_data[‘success’] == True:
            print(sobject)
            print(“ID= ” + id)
            contact_data2 = getattr(sf, o).get(id)
            return contact_data2
        else:
            return contact_data2[‘errors’]
  2. def update_salesforce_object(sf, sobject, object_data):
        try:
            o = sobject
            sobject_data = getattr(sf, o).update(object_data)
        except Exception as e:
            print(e)
            return e
        id = sobject_data[‘id’]
        if sobject_data[‘success’] == True:
            print(id)
            contact_data2 = getattr(sf, o).get(id)
            return contact_data2
        else:
            return contact_data2[‘errors’]
  3. def _query(sf, sobject, query):
        query = “SELECT Id FROM ” + sobject
        data = sf.query(query)
        return data
  4. def get_by_id(sf, sobject, id):
        o = sobject
  5.     try:
            data = getattr(sf, o).get(id)
        except Exception as e:
            print(e)
        return data

The nice feature about the Simple Salesforce library for Python is that it is flexible. You can run these functions as Python initiated functions. And you can use these Python functions as Robot Framework keyword functions. I like that flexibility. The combination of the tools result in easy-to-use functions and easy to see test results.

For details about Simple Salesforce, look at the links below for more understanding and reference purposes:

  1. Simple Salesforce Documentation
  2. Simple Salesforce Components

When things don’t work as expected

Before I reached the end of the previous section, I discovered some issues. The issues may arise when data syntax or keyword pairs are incorrect. How we present the input data in the Excel spreadsheet, Robot Framework variables, and Python are important. Not all functions are affected by the syntax and keyword pairs. But the create and the update functions are affected.

My initial thinking for passing data from the spreadsheet to Python was to set the format of data in a JSON or dictionary format, but I soon found out it was problematic. Setting the data in the simplest format and then for multiple keyword/value pairs of data, format it like Comma-delimited data. Let the programming take care of reformatting the data. I developed a reusable function of 22 lines to manage formatting data into a dictionary for input to the create and update Simple Salesforce functions.

An example of the input data format inside an Excel cell follows:

Name:Auto for Sale,
Description:Blue Ford Extra 4DR, 
Amount:30000.00,
StageName:Needs Analysis,
CloseDate:2021-12-15

The code that processes this input and formats it into a dictionary follows:

def convert_object(object_data):
    object_data2 = []
    object_data3 = []
    list_keys = []
    list_vals = []
    object_data2 = ”.join(object_data)
    object_data2 = object_data2.rsplit(“,”)
    for i in range(len(object_data2)):
        ndx = object_data2[i].find(“:”)
        lk = object_data2[i]
        lkk = str(lk[:ndx])
        lkk = lkk.strip()
        list_keys.append(lkk)
        lkk2 = str(lk[ndx + 1:])
        lkk2 = lkk2.strip()
        list_vals.append(lkk2)
    for y in range(len(list_keys)):
        list_keys[y].strip()
        list_vals[y].strip()
 
    object_data2 = zip(list_keys, list_vals)
    object_data3 = dict(object_data2)
    return object_data3

For more information regarding Simple Salesforce, check out the video.

17 thoughts on “Python and Simple Salesforce in Robot Framework (Tutorial 5)

  1. Hey.

    I’m trying to reuse what you show in this blog to start automating salesforce.
    It’s goes really awful and i was thinking that you could help me repeat what you have in this section.
    Do You have something i could just download to try how it works?
    I’m actually fresh to python, that’s why i’m asking for help.

    Thanks for the reply

  2. Hi, first let me appreciate and thank you for this awesome shearing!
    After spending some time trying to make it to work, I was able to fix most of my errors. During the process, I took some installation notes that maybe some other people find useful. Let me know if these are useful to you.

    I’m almost there I believe, currently, I’m getting the following error:

    ” WARN ] Multiple test cases with name ‘Test Case – ${testcase} – ${description}’ executed in test suite ‘SimpleSF1’.
    Test Case – ${testcase} – ${description} | FAIL |
    Parent suite setup failed:
    No keyword with name ‘${username} ${password} ${api_token}’ found.”

    Could you help me?

    Many many thanks in advance!

    1. If you have not checked my video at https://www.youtube.com/watch?v=r72QsVBZM9U , please take a look. There I reference components you can download related to this article and video. If you still need help go to my CONTACT US or send another reply.

      RESPONSES TO ERRORS:

      ” WARN ] Multiple test cases with name ‘Test Case – ${testcase} – ${description}’ executed in test suite ‘SimpleSF1’.
      Test Case – ${testcase} – ${description} | FAIL |
      —-> The $ variables above must be defined in the header columns of the EXCEL input file. Make sure the filename is the same as the Robot Framework main file.

      Parent suite setup failed:
      No keyword with name ‘${username} ${password} ${api_token}’ found.”
      —-> These variables must be defined in the PYTHON program. They are your Salesforce user credentials. They are only referenced in the PYTHON program.

  3. Hi! Well, I was able to make progress on the previous comment I left.
    I believe that the source of the issues is that, as you know, both robot and python are indentation sensitive (if this terminology really exists xD). I used the pdf file that you provided, the problem is that when you copy the code from the pdf and you paste it in VSC, Sublime, or Pycharm you lose all the indentation (break lines disappear, and everything ends up separated by one only space) by this reason I had to reconstruct all of the files. Would it be possible to provide each file in a different format?
    Otherwise, currently, I got to a dead end. robot logs show me that the “Open Salesforce and Launch excel” , “Stop excel”, and “Close all excel documents” are working. The problem is with the Testcases themselves. First, in the title, the fields ${testcase} and ${description} are not getting auto-populated. Then, the test case itself is throwing an error saying “Variable ‘${sObject}’ not found.” I added a log for the sObject in the Launch excel and it’s saying “Opportunity”, I don’t know where else to look. Could you please help me? Thanks!!

    1. Thank you for pointing out your issues. Unfortunately my only choice at present for my resource files is PDF. And PDF is compressing spaces which is bad news. This the root cause for the variable not found conditions. In Robot Framework syntax double-spacing is important before variables. That is being lost with PDF. I am looking into a copy/paste method that will maintain the proper spacing. Until then, edit the Robot Framework files to ensure double-spacing where syntax requires it. And four space at beginning of lines where required. Sorry for the difficulties.

  4. Hey, me again. First, let me thank you for taking the time to upload the files to Github! i highly appreciate it. That fixed most of the issues.
    Now, I believe I’m almost there…lol… I’m getting errors for the Authentication. In the new code that you uploaded, there are two functions in the py file that use the credentials. I populated both. I tried adding and removing the token. And it still didn’t work.. so I used postman to check the credentials with the clientID and secret, and it worked like a charm… Any thoughts?
    Thanks 🙂

      1. If the information at jereze.com is not clear enough for you, let me know. I may need to write an article covering this subject. But it will take me some time to complete.

        1. I was thinking about one more thing that is important with programmatic salesforce login. The program is treated as an REST API. In the normal user login to Salesforce you need to do the following to support python program login access:

          * Enable API access in Salesforce by user profile.
          * Click on Setup.
          * Go to Manage Users and click Profiles.
          * Click Edit on the specific profile you’re updating.
          * Scroll down to Administrative Permissions and check the API Enabled box.
          Click Save.

          1. Hi! I found the issue. I’m trying to identify what the F·$% is going on… The piece of code within the get_Oauth_token function works perfectly with my credentials if I copy the content and run it as “plain code” in a separate python file. But if I try to put that code within a function and do the invocation of that function, it doesn’t work. This happens even if I put the function within the same file. I have never seen this behavior in Python. BTW, Which Python version are you using? Thanks

          2. Update: Made the function to work. I’m investigating why when I execute the robot code I get an error saying that the credentials are incorrect.

          3. Update2: FOUND IT! It seems that the binding of variables between robot and python do not work. I hardcoded the credentials in the “Auth with Token” in robot and it worked!
            PS: I noticed that the robot code is using the sf connection function from the RPA library instead of the one defined in the python script. So, whats the purpose of those functions?
            Thanks

          4. well, I’m at the end of the road! I was able to make it to work.
            Final Notes:
            1. The Library Collections need to be included under Settings in the “Resource.robot” file, otherwise, the Dictionary functions fail.
            2. Packages that need to be installed:
            – pip install robotframework-seleniumlibrary
            – pip install robotframework-datadriver
            – pip install robotframework-excellib-xlsm==2.0.3
            – pip install simple-salesforce
            – pip install selenium
            – pip install robot-framework
            3. Defining the credentials in the Python file didn’t work for me. In the “Resources.robot” file, there is a section that makes a call to an RPA function that says “Auth With Token”(This is the SF login that is being used). I replaced the credentials’ variables straight with the values (Hardcoded the values xD).
            4. I made it to work with VisualStudioCode. Check at the bottom left, it tells you which Python version is being used. pip only installs the libraries in the predefined path for it. So, you may encounter issues for functions that belong to pre-installed libraries that can not be found.
            5. I’m using Python’s latest version= 3.9.7
            6. BE CAREFULL, both python and robot are indentation sensitive (In python indentation defines scopes, on the other hand, in robot spaces and indentation defines a LOT of different things).

            Many many Thanks to Wally!!

  5. In response to: BTW, Which Python version are you using?
    I am using the following software versions for RPA Salesforce testing in PyCharm:
    -> PyCharm 2020.2
    -> Python 3.8.3
    -> RF Selenium Library 5.4.3
    -> RPA Framework 10.4.0

    I probably need to see the entire set of components you have to give you a definitive answer.

  6. So, whats the purpose of those functions?
    I’ve used the RPA and Simple-Salesforce libraries in both Robot Framework and Python. They should work either way for authentication. The SF variable I used in Python to capture the Salesforce connection value (Login credential) and pass it to Robot Framework to execute other functions in Robot Framework.

    I have had much success using the component versions I previously mentioned. When I try to upgrade them I run into problems.

Comments are closed.