Skip to content
This repository has been archived by the owner on Dec 3, 2021. It is now read-only.

New Lesson - Robot Framework #125

Merged
merged 9 commits into from
Nov 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ _old_contrail/
*ansiblekey.json

platform/antidote-web/target/

__pycache__
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

- Adding Lesson-30 Working with Salt [#114](https://github.com/nre-learning/antidote/pull/114)
- Adding Lesson-16 Jinja2 Templates [#121](https://github.com/nre-learning/antidote/pull/121)
- Adding Lesson-29 Robot Framework [#125](https://github.com/nre-learning/antidote/pull/125)


### Other

Expand Down
1 change: 1 addition & 0 deletions images/utility/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ napalm
netmiko
jsnapy
junos-eznc
robotframework
jinja2
Binary file added lessons/lesson-29/lessondiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions lessons/lesson-29/stage1/chapter1_eg1.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
*** Settings ***
Library substring.py

*** Variables ***
${str1}= Robot
${str2}= Robot

*** Test Cases ***
Check Equal1
Should Be Equal As Strings ${str1} Framework

Check Equal2
Should Be Equal As Strings ${str1} robot

Check Equal3
Should Be Equal As Strings ${str1} ${str2}

CheckSubstring1
is a substring Jun Juniper

CheckSubstring2
is a substring June Juniper
147 changes: 147 additions & 0 deletions lessons/lesson-29/stage1/configs/vqfx1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<configuration operation="replace">
<version>15.1X53-D60.4</version>
<system>
<host-name>vqfx1</host-name>
<root-authentication>
<encrypted-password>$1$E./KlYWD$4IB3UHD/TK0rVbXchd0ex0</encrypted-password>
<ssh-rsa>
<name>ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key</name>
</ssh-rsa>
</root-authentication>
<login>
<user>
<name>vrnetlab</name>
<uid>2001</uid>
<class>super-user</class>
<authentication>
<encrypted-password>$1$PvaswVKY$Nz5ELIzEL9M21FxSWAKdd1</encrypted-password>
</authentication>
</user>
</login>
<services>
<ssh>
<root-login>allow</root-login>
</ssh>
<netconf>
<ssh>
</ssh>
<rfc-compliant/>
</netconf>
<rest>
<http>
<port>8080</port>
</http>
<enable-explorer/>
</rest>
</services>
<syslog>
<user>
<name>*</name>
<contents>
<name>any</name>
<emergency/>
</contents>
</user>
<file>
<name>messages</name>
<contents>
<name>any</name>
<notice/>
</contents>
<contents>
<name>authorization</name>
<info/>
</contents>
</file>
<file>
<name>interactive-commands</name>
<contents>
<name>interactive-commands</name>
<any/>
</contents>
</file>
</syslog>
<extensions>
<providers>
<name>juniper</name>
<license-type>
<name>juniper</name>
<deployment-scope>commercial</deployment-scope>
</license-type>
</providers>
<providers>
<name>chef</name>
<license-type>
<name>juniper</name>
<deployment-scope>commercial</deployment-scope>
</license-type>
</providers>
</extensions>
</system>
<interfaces operation="merge">
<interface>
<name>em0</name>
<unit>
<name>0</name>
<family>
<inet>
<address>
<name>10.0.0.15/24</name>
</address>
</inet>
</family>
</unit>
</interface>
<interface>
<name>em3</name>
<unit>
<name>0</name>
<family>
<inet>
<address>
<name>10.31.0.11/24</name>
</address>
</inet>
</family>
</unit>
</interface>
<interface>
<name>em4</name>
<unit>
<name>0</name>
<family>
<inet>
<address>
<name>10.12.0.11/24</name>
</address>
</inet>
</family>
</unit>
</interface>
</interfaces>
<forwarding-options>
<storm-control-profiles>
<name>default</name>
<all>
</all>
</storm-control-profiles>
</forwarding-options>
<routing-options>
<autonomous-system>
<as-number>64001</as-number>
</autonomous-system>
</routing-options>
<protocols>
<igmp-snooping>
<vlan>
<name>default</name>
</vlan>
</igmp-snooping>
</protocols>
<vlans>
<vlan>
<name>default</name>
<vlan-id>1</vlan-id>
</vlan>
</vlans>
</configuration>
156 changes: 156 additions & 0 deletions lessons/lesson-29/stage1/guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Chapter 1 - Introduction to Robot Framework
**Contributed by: [@saimkhan92](https://github.com/saimkhan92) and [@lara29](https://github.com/lara29)**


lara29 marked this conversation as resolved.
Show resolved Hide resolved
Robot Framework is a Python based test automation framework, which is extensively used by the mobile, web, and embedded systems industry to perform acceptance testing. It provides a simple interface to write test-cases that can be arranged into a test-suite and is easy to implement.

Robot is a `keyword-driven` testing framework, where all the actions and functions of the test suite are performed using simple, human-understandable keywords.

A generic architecture of the Robot framework is shown in the lesson diagram.

Reference: [Robot Framework User Guide](http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#id413)

Let's move on to the various concepts used in Robot Framework


---
lara29 marked this conversation as resolved.
Show resolved Hide resolved

## Anatomy of a Robot file
A typical Robot file is shown below:
>```
>*** Settings ***
>Library myLibrary.py
>
>*** Variables ***
>${name1}= Jane
>${name2}= Jane Smith
>@{namelist}= Steve Walt Craig Zac
>&{namedict}=
>
>*** Keywords ***
>Print Log
> Log ${name}
>
>*** Test Cases ***
>Check Name
> Should Be Equal ${name1} Jane
>```

The different sections `Settings`, `Variables`, `Keywords`, and `Testcases` are called *tables*. They are a way to arrange the different components of Robot in a neat, orderly manner.

### Settings
This section is used to import standard libaries, external libraries, resource files, variable files, and Setup/Teardown settings. The purpose of these libraries and files has been explained in the later sections.

### Variables
This section defines the variables used in the test cases. There are four types of variables:

* *Scalar variable*: A scalar variable is replaced by its value in test-cases. They are mostly used to hold strings, but they can also hold objects like lists. They are represented using the variable identifier `$`. Eg:
>```
>*** Variables ***
>${NAME} Jake Doyle
>```

* *List variables*: Used to store lists (Eg: Python lists). The list variables can be used to reference the whole list or can be used to access individual list elements using the index. They are represented using the variable identifier `@`. Eg:
>```
>*** Variables ***
>@{NAMES} Jake Tinny Des
>```

* *Dictionary variables*: Used to store dictionary-like objects (like Python dict()), and they are referenced using the identifier `&`. Eg:

>```
>*** Variables ***
>&{Employees} firstname=Jake lastname=Doyle
>```

* *Environment variables*: Used to store string environment variables, and are referenced using the identifier `%`. Eg:
>```
>*** Test Cases ***
>Log %{HOME}
>```


### Keywords

Keywords are used to implement the real testing capability of the Robot framework. These keywords are provided by various test libraries which are a part of the Robot Ecosystem. These libraries, which expose keywords to the robot files are of the following types:
* Standard Library - Consists of a set of libraries come bundled-in as part of the Robot Framework. Example: OperatingSystem, Collections, DateTime etc.
* External Library - Can be downloaded using pip. Example: Android library, HTTP library etc.
* Private Library - Can be a self created Python or Java file. Example: JunosDevice.py, a private library, described in stage2 of this tutorial.
* Built-in keyword Library - This is a part of the standard library which provides a set of frequently used generic keywords used for string comparison, logging, etc.

To use the keywords defined by the standard, external, and private libraries, the respective libraries first need to be imported in the Settings table of a Robot file. Built-in keywords can be used out-of-the-box without the need to import any library in the *Settings* section. Also, it should be noted that the keywords behave like functions - they can take in arguments and can return values back.

A few examples of keywords are -
+ Built-in keywords
>```
>Should Be Equal Jane John
>```
This built-in keyword `Should Be Equal` will check if the next two arguments are the same, and will throw an Exception if they are unequal.

+ Private library keywords

Suppose we have a custom Python file, which contains the below function -
>```
>def is_a_substring(str1,str2)
>```
This function maps into the keyword `Is A Substring` automatically, and can be invoked in Robot test-cases as below -
>```
>Is A Substring Jane Janet
>```

### Test Cases
This section contains the test-cases we would like to execute, using the keywords, variables, and the libraries that we imported earlier.

The test cases are executed in the same order that they occur in the test-file.

In the below example, `Check Name` is the name of the test case, which uses the built-in keyword `Should Be Equal` to compare two strings - the scalar variable name1, and "Jane".

>```
>*** Test Cases ***
>Check Name
> Should Be Equal ${name1} Jane
>```


### General Syntax rules

1. There should be atleast two spaces between the following:
- Keywords and Keywords
- Keywords and Arguments
- Arguments and Arguments
2. Keywords and variables are case-insensitive


---

## Running your first Robot file

In our first example, we have a Robot file `chapter1_eg1.robot`, and a python file `substring.py`.

Let's examine our python file `substring.py` -
```
cat /antidote/lessons/lesson-29/stage1/substring.py
```
<button type="button" class="btn btn-primary btn-sm" onclick="runSnippetInTab('linux1', 9)">Run this snippet</button>

It contains a single function `is_a_substring` that takes in two strings arguments - str1 and str1. It then returns True if str1 is a substring of str2.

Let's checkout our Robot file now -
```
cat /antidote/lessons/lesson-29/stage1/chapter1_eg1.robot
```
<button type="button" class="btn btn-primary btn-sm" onclick="runSnippetInTab('linux1', 10)">Run this snippet</button>

Under the Settings, we import our python file `substring.py` as a Library.

We have five test-cases in total, and these will be executed one after the other. The first three test cases perform string comparison, while the last two execute the substring function.

We'll go ahead and start our test-cases:

```
robot /antidote/lessons/lesson-29/stage1/chapter1_eg1.robot
```

<button type="button" class="btn btn-primary btn-sm" onclick="runSnippetInTab('linux1', 11)">Run this snippet</button>

As expected, test-cases `Check Equal1`, `Check Equal2`, `Check Substring2` fails, and `Check Equal1`, `Check Substring1` passes. Since all the test-cases did not pass, the test-suite result is "Fail".
3 changes: 3 additions & 0 deletions lessons/lesson-29/stage1/substring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def is_a_substring(str1, str2):
if str1 not in str2:
raise Exception("{} is not a substring of {}".format(str1,str2))
31 changes: 31 additions & 0 deletions lessons/lesson-29/stage2/JunosDevice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from jnpr.junos import Device

class JunosDevice(object):

def __init__(self):
self.device = None

def connect_device(self,host, user, password):
self.device = Device(host, user=user, password=password, port=22)
self.device.open()

def gather_device_info(self):
df = dict(self.device.facts)
device_facts = {
"os_version": df["version"],
"serialnumber": df["serialnumber"],
"model": df["model"],
"hostname": df["hostname"]
}
return device_facts

def close_device(self):
self.device.close()

def get_hostname(self):
facts = self.gather_device_info()
return facts["hostname"]

def get_model(self):
facts = self.gather_device_info()
return facts["model"]
Loading