Browse Source

Random functionality along with documentation and tests

Akash Singh 3 years ago
parent
commit
9fed05a8de

+ 97 - 0
baangt/TestSteps/RandomValues.py

@@ -0,0 +1,97 @@
+from baangt.base.Faker import Faker
+import random
+import datetime
+import logging
+
+logger = logging.getLogger("pyC")
+
+class RandomValues:
+    def __init__(self):
+        self.fake = Faker().faker
+
+    def retrieveRandomValue(self, RandomizationType="string", mini=None, maxi=None, format=None):
+        '''
+        updates attribute then calls another function to generate random value and return it to the caller.
+        :param RandomizationType:
+        :param mini:
+        :param maxi:
+        :param format:
+        :return:
+        '''
+        self.RandomizationType = RandomizationType
+        self.min = mini
+        self.max = maxi
+        self.format = format
+        return self.generateValue()
+
+    def generateValue(self):
+        '''
+        Generates random value as per input type and other parameters
+        :return:
+        '''
+        if self.RandomizationType.lower() == "string":
+            self.min = self.min or 3  # If value is none than change it to 3. We can do this in parameter but as we have
+            self.max = self.max or 10 # multiple types and we need different default value for each of them, this is used.
+            return self.fake.pystr(self.min, self.max)  # used faker module of baangt to generate string name
+
+        elif self.RandomizationType.lower() == "name":
+            return self.fake.name()  # used faker module of baangt to generate fake name
+
+        elif self.RandomizationType.lower() == "int":
+            self.min = self.min or 0
+            self.max = self.max or 1000000
+            return random.randint(self.min, self.max)
+
+        elif self.RandomizationType.lower() == "float":
+            self.min = self.min or 0
+            self.max = self.max or 1000
+            flt = random.uniform(self.min, self.max)
+            return flt
+
+        elif self.RandomizationType.lower() == "date":
+            self.format = self.format or "%d/%m/%y"
+            if self.min:
+                try:
+                    self.min = datetime.datetime.strptime(self.min, self.format).timestamp()
+                except Exception as ex:
+                    logger.info(f"Minimum date's structure or format is incorrect - {str(ex)}")
+                    self.min = 86400
+            else:
+                self.min = 86400
+            if self.max:
+                try:
+                    self.max = datetime.datetime.strptime(self.max, self.format).timestamp()
+                except Exception as ex:
+                    logger.info(f"Maximum date's structure or format is incorrect - {str(ex)}")
+                    self.max = datetime.datetime.now().timestamp()
+            else:
+                self.max = datetime.datetime.now().timestamp()
+            return datetime.datetime.fromtimestamp(random.randint(self.min, self.max)).strftime(self.format)
+
+        elif self.RandomizationType.lower() == "time":
+            self.format = self.format or "%H:%M:%S"
+            base = datetime.datetime(2000, 1, 1)
+            if self.min:
+                try:
+                    time = datetime.datetime.strptime(self.min, self.format)
+                    mini = base.replace(hour=time.hour, minute=time.minute, second=time.second).timestamp()
+                except Exception as ex:
+                    logger.info(f"Minimum time's structure or format is incorrect - {str(ex)}")
+                    mini = base.replace(hour=0, minute=0, second=0).timestamp()
+            else:
+                mini = base.replace(hour=0, minute=0, second=0).timestamp()
+            if self.max:
+                try:
+                    time = datetime.datetime.strptime(self.max, self.format)
+                    maxi = base.replace(hour=time.hour, minute=time.minute, second=time.second).timestamp()
+                except Exception as ex:
+                    logger.info(f"Maximum time's structure or format is incorrect - {str(ex)}")
+                    maxi = base.replace(hour=11, minute=59, second=59).timestamp()
+            else:
+                maxi = base.replace(hour=11, minute=59, second=59).timestamp()
+            return datetime.datetime.fromtimestamp(random.uniform(mini, maxi)).strftime(self.format)
+
+        else:  # if type is not valid this statement will be executed
+            raise BaseException(
+                f"Incorrect type {self.RandomizationType}. Please use string, name, int, float, date, time.")
+

+ 28 - 7
baangt/TestSteps/TestStepMaster.py

@@ -13,6 +13,8 @@ from baangt.base.Utils import utils
 from baangt.base.RuntimeStatistics import Statistic
 import random
 import itertools
+import json
+from baangt.TestSteps.RandomValues import RandomValues
 
 logger = logging.getLogger("pyC")
 
@@ -56,6 +58,7 @@ class TestStepMaster:
                                                          sequence=kwargs.get(GC.STRUCTURE_TESTCASESEQUENCE))
         self.testCase = self.testRunUtil.getTestCaseByNumber(lSequence, kwargs.get(GC.STRUCTURE_TESTCASE))
         self.testStep = self.testRunUtil.getTestStepByNumber(self.testCase, self.testStepNumber)
+        self.randomValues = RandomValues()
 
         if self.testStep and len(self.testStep) > 1:
             if not isinstance(self.testStep[1], str) and executeDirect:
@@ -358,13 +361,18 @@ class TestStepMaster:
 
     def replaceAllVariables(self, lValue, lValue2, replaceFromDict=None):
         # Replace variables from data file
-        try:
-            if len(lValue) > 0:
+        if len(lValue) > 0:
+            try:
                 lValue = self.replaceVariables(lValue, replaceFromDict=replaceFromDict)
-            if len(lValue2) > 0:
+            except Exception as ex:
+                logger.info(f"Exception in {lValue}")
+                logger.info(ex)
+        if len(lValue2) > 0:
+            try:
                 lValue2 = self.replaceVariables(lValue2, replaceFromDict=replaceFromDict)
-        except Exception as ex:
-            logger.info(ex)
+            except Exception as ex:
+                logger.info(f"Exception in {lValue2}")
+                logger.info(ex)
         return lValue, lValue2
 
     def __getIBAN(self, lValue, lValue2):
@@ -539,8 +547,19 @@ class TestStepMaster:
                     for key in center.split('.')[-1:]:
                         dic = self.iterate_json(dic, key)
                     centerValue = dic
+            if "random{" in center.lower() and "}" in center:  # random keyword is processed here
+                args = json.loads(center[6:])
+                # dictionary used in converting data in to real parameters which are used by the method
+                change_args = {"type": "RandomizationType", "min": "mini", "max": "maxi", "format": "format"}
+                data = {}  # dictionary containing arguments for method and will be used as **args
+                for keys in args:
+                    if keys not in change_args:  # if key is not a valid argument for method
+                        logger.info(f'"{keys}" is not a valid argument for Random data generator. So it is not used')
+                    else:
+                        data[change_args[keys]] = args[keys]
+                centerValue = self.randomValues.retrieveRandomValue(**data)
 
-            if centerValue: # if we got value from the passed json then bypass this if else conditions
+            if centerValue:  # if we got value from the passed json then bypass this if else conditions
                 pass
             elif "." not in center:
                 # Replace the variable with the value from data structure
@@ -573,8 +592,10 @@ class TestStepMaster:
                 expression = "".join([left_part, centerValue, right_part])
             except:
                 expression = centerValue
-                if type(expression) == float or type(expression) == int:
+                if type(expression) == int:
                     expression = str(int(expression))
+                elif type(expression) == float:
+                    expression = str(float(expression))
         return expression
 
     def iterate_json(self, data, key):

+ 48 - 0
docs/SimpleAPI.rst

@@ -116,3 +116,51 @@ manually declaring them:
     * - ANSWER_CONTENT
       - Last content of an API-Call (Post, Get, etc.). Again you can access/extract/replace parts of this content using
         the "." like described in the line above (e.g. ``$(ANSWER_CONTENT.FRANZI)`` to refer to a content part ``FRANZI``.
+
+Random
+------
+Sometimes we need random values like string, name, integer, float, date, time now in such case we have ``random``
+functionality. It is used inside value column of and its structure is
+``$(random{"type":<Type>},"min":<Minimum>,"max"<Maximum>,"format":<Format>)``. Only ``type`` field is compulsory and
+every other fields are optional, also each fields are not useful in every type, e.g.- ``name`` type doesn't need any
+other optional fields as they are use less for it. You can see fields and types supporting them.
+
+
+.. list-table:: Fields supporting types
+   :widths: 25 75
+   :header-rows: 1
+
+   * - Field
+     - Type
+
+   * - type
+     - This field is compulsory and base of ``random`` funtionality.
+       string, name, int, float, date, time are the types currently supported
+
+   * - min
+     - string, int, float, date, time are the types supporting this field. Value of min will be with respect to its
+       type like value for string will be an integer containing minimum number of characters in string and for all other
+       it will be lower limit, for int it will be an integer & float for float, for date value will be a date e.g. -
+       "31/01/2020" and for time it would look like "20:30:59"
+
+   * - max
+     - string, int, float, date, time are the types supporting this field. Value of max will be same like in min,
+       value for string will be an integer containing maximum number of characters in string and for all other it
+       will be upper limit, for int it will be an integer & float for float, for date value will be a date e.g. -
+       "01/06/2020" and for time it would look like "13:10:30"
+
+   * - format
+     - date, time are the only types supporting format field. In above date examples date is in %d/%m/%Y format and
+       time is in %H:%M:%S format. Here "%d" stands for the day, "%m" stands for month, "%Y" stands for year including
+       century e.g.- 2020, if you want only year you can use "%y" e.g. 20. If you use min and max fields in date, time
+       then you must input its written format in format field, default will be ""%d/%m/%y" for date. Now if you want
+       date with "-" as seperator you can write format as "%d-%m-%Y" so the output would be like "31-01-2020".
+
+       `examples`
+        $(random{"type":"name"})
+        $(random{"type":"string", "min":10, "max":100})
+        $(random{"type":"int", "min":10, "max":100})
+        $(random{"type":"float"})
+        $(random{"type":"date", "min":"20/1/2020", "max":"30/6/2020", "format":"%d/%m/%Y"})
+        $(random{"type":"time"})
+        $(random{"type":"time", "min":"10.30.00", "max":"15.00.00"}, "format": "%H.%M.%S")

+ 51 - 1
docs/simpleExample.rst

@@ -188,6 +188,8 @@ More details on Activities
      - Write the text given in column ``value`` to the element specified by ``locator``. Only rarely will you have fixed
        values. Usually you'll assign columns of the test data using variable replacement (e.g. ``$(POSTCODE)`` to set the
        text from column ``POSTCODE`` from the datafile into the destination element.
+       In some cases we need to write any random value in the field or we need random value like name, string, interger,
+       date, etc. for some other purpose for that we ``Random`` funtion. You will learn about it further in this document.
    * - setTextIF
      - Same as SetText, but will only do something in cases where there is a value in the datafile. Similarly to clickIF
        this little helper functionality can help you save hours and hours in creation and maintenance of rocksolid and
@@ -275,7 +277,8 @@ More details on Activities
          Default field-value: {'HouseNumber': '6', 'AdditionalData1': 'Near MahavirChowk', 'AdditionalData2': 'Opposite St. Marish Church', 'StreetName': 'GoreGaon', 'CityName': 'Ambala', 'PostalCode': '160055', 'CountryCode': 'India'} 
 
        These fields can be used as filter criteria in field value.
-       Example value= `{CountryCode:CY, PostlCode: 7}`. 
+       Example value= `{CountryCode:CY, PostlCode: 7}`.
+
 
 
        Resulted field-value :{'HouseNumber': '6', 'AdditionalData1': 'Near MahavirChowk', 'AdditionalData2': 'Opposite St. Marish Church', 'StreetName': 'GoreGaon', 'CityName': 'Ambala', 'PostalCode': '7', 'CountryCode': 'CY'}
@@ -284,3 +287,50 @@ More details on Activities
        If a prefix was povided in field Value2, the fieldnames will be concatenated with this prefix,
        e.g.if value2=`PremiumPayer_`, then the resulting field for CountryCode in testDataDict would become PremiumPayer_CountryCode.
 
+Random
+------
+Sometimes we need random values like string, name, integer, float, date, time now in such case we have ``random``
+functionality. It is used inside value column of and its structure is
+``$(random{"type":<Type>},"min":<Minimum>,"max"<Maximum>,"format":<Format>)``. Only ``type`` field is compulsory and
+every other fields are optional, also each fields are not useful in every type, e.g.- ``name`` type doesn't need any
+other optional fields as they are use less for it. You can see fields and types supporting them.
+
+
+.. list-table:: Fields supporting types
+   :widths: 25 75
+   :header-rows: 1
+
+   * - Field
+     - Type
+
+   * - type
+     - This field is compulsory and base of ``random`` funtionality.
+       string, name, int, float, date, time are the types currently supported
+
+   * - min
+     - string, int, float, date, time are the types supporting this field. Value of min will be with respect to its
+       type like value for string will be an integer containing minimum number of characters in string and for all other
+       it will be lower limit, for int it will be an integer & float for float, for date value will be a date e.g. -
+       "31/01/2020" and for time it would look like "20:30:59"
+
+   * - max
+     - string, int, float, date, time are the types supporting this field. Value of max will be same like in min,
+       value for string will be an integer containing maximum number of characters in string and for all other it
+       will be upper limit, for int it will be an integer & float for float, for date value will be a date e.g. -
+       "01/06/2020" and for time it would look like "13:10:30"
+
+   * - format
+     - date, time are the only types supporting format field. In above date examples date is in %d/%m/%Y format and
+       time is in %H:%M:%S format. Here "%d" stands for the day, "%m" stands for month, "%Y" stands for year including
+       century e.g.- 2020, if you want only year you can use "%y" e.g. 20. If you use min and max fields in date, time
+       then you must input its written format in format field, default will be ""%d/%m/%y" for date. Now if you want
+       date with "-" as seperator you can write format as "%d-%m-%Y" so the output would be like "31-01-2020".
+
+       `examples`
+        $(random{"type":"name"})
+        $(random{"type":"string", "min":10, "max":100})
+        $(random{"type":"int", "min":10, "max":100})
+        $(random{"type":"float"})
+        $(random{"type":"date", "min":"20/1/2020", "max":"30/6/2020", "format":"%d/%m/%Y"})
+        $(random{"type":"time"})
+        $(random{"type":"time", "min":"10.30.00", "max":"15.00.00"}, "format": "%H.%M.%S")

BIN
examples/CompleteBaangtWebdemo_random.xlsx


+ 42 - 0
tests/test_Random.py

@@ -0,0 +1,42 @@
+from baangt.TestSteps.TestStepMaster import TestStepMaster
+from baangt.TestSteps.RandomValues import RandomValues
+import datetime
+
+TestStepMaster.testcaseDataDict = {}
+TestStepMaster.randomValues = RandomValues()
+
+
+def test_name():
+    name = TestStepMaster.replaceVariables(TestStepMaster, '$(random{"type":"name"})')
+    assert len(name) > 0 and len(name.split()) > 1
+
+
+def test_string():
+    string1 = TestStepMaster.replaceVariables(TestStepMaster, '$(random{"type":"string"})')
+    assert len(string1) > 0
+    string2 = TestStepMaster.replaceVariables(TestStepMaster, '$(random{"type":"string", "min":10, "max":100})')
+    assert len(string2) > 0 and len(string2) < 101
+
+
+def test_int():
+    integer = TestStepMaster.replaceVariables(TestStepMaster, '$(random{"type":"int", "min":100, "max":1000})')
+    assert int(integer) > 99 and int(integer) < 1001
+
+
+def test_float():
+    flt = TestStepMaster.replaceVariables(TestStepMaster, '$(random{"type":"float", "min":100, "max":1000})')
+    assert float(flt) > 99 and float(flt) < 1001
+
+
+def test_date():
+    date = TestStepMaster.replaceVariables(
+        TestStepMaster, '$(random{"type":"date", "min":"20/1/2020", "max":"30/6/2020", "format":"%d/%m/%Y"})')
+    date_obj = datetime.datetime.strptime(date, "%d/%m/%Y")
+    assert type(date_obj) is datetime.datetime and date_obj.year == 2020
+
+
+def test_time():
+    time = TestStepMaster.replaceVariables(
+        TestStepMaster, '$(random{"type":"time", "min":"10.30.00", "max":"15.00.00", "format": "%H.%M.%S"}')
+    time_obj = datetime.datetime.strptime(time, "%H.%M.%S")
+    assert type(time_obj) is datetime.datetime and 9 < time_obj.hour < 15