diff --git a/08_code_management/00_Git_Basics.md b/08_code_management/00_Git_Basics.md new file mode 100644 index 0000000..6e25088 --- /dev/null +++ b/08_code_management/00_Git_Basics.md @@ -0,0 +1,81 @@ +# Git Basics + +## What is SCM and why Git is different +### Software Configuration Management +- Track changes to code +- Facilitate collaboration working on the same code base +- Identifying causes of defects +- Facilitate build & release process including continuous integration, continuous delivery. + +### GIT +- Decentralised +- not locking +- Light weight branching +- No "Trunk"/Special branches - All branches are equal + + +## Getting Started +### Some terms we will be using +- Working Tree +- Index +- Staging +- Head +### Demonstration 00.1 +Taking a directory on the file system initialise as a GIT repo and commit files + + - Initialise a repo (run at the root of the project) + > `git init` + - Stage files - Current state of selected files are stage for the next commit + > `git add ` + - Commit - Commits changes to the repository + ``` + git commit + git commit -m "My commit message" + ``` + - Viewing the status of the working tree +``` +git status +``` + + +*Note on staging*: Once a file is stage and further changes made before the commit will not be committed unless those changes are also staged. + +### Exercise 00.1 +Take your existing code initialise, stage and commit + +## Log and Diff +### Demonstration 00.2 +Makes changes in the working directory and view differences from HEAD + +- View all differences +> `git diff` + +- View diffences for specific files +> `git diff ` + +Commit some more changes a view the log +- View the log +> `git log` + +View difference between HEAD and a previous commit +> `git diff HEAD` + +View difference between working directory and a previous commit +> `git diff ` + +View difference between two commits +> `git diff ` + +### Exercise 00.2 +1. Initialise a new git project to experiment with (we will call this the sandbox project). Try out making changes in working tree and commit changes using the `git diff` command to view the changes. +2. Initialise a new git project for you project code. + +### Other index/staging commands +These command update both the Working tree and the index +- Move/rename a files +> `git mv ` +- Remove files +> `git rm ` + +## Demonstration 00.2 +Working with GIT in PyCharm/CLion diff --git a/08_code_management/01_Branching.md b/08_code_management/01_Branching.md new file mode 100644 index 0000000..935519e --- /dev/null +++ b/08_code_management/01_Branching.md @@ -0,0 +1,85 @@ +# Branching and Merging + +## Working with branches in Git +- Git has no special branches. No trunk (though it does have a default branch "master") +- Git branching is lightweight mean developers can create, merge and discard branches freely +- Can branch from any branch +- Can merge to any branch + +Create a branch as a copy of the current branch +``` +git branch +``` + +Switch branch +``` +git checkout +``` + +Create a branch and switch to that branch +``` +git checkout -b +``` + +Comparing current branch to another +``` +git diff +``` + +Comparing two other branches +``` +git diff +``` + +This can also be done in PyCharm and CLion. + +## Merging and rebasing + +Merging combined the history for the source branch with that of the target. + +To merge a branch to the current working branch: +``` +git merge +``` + +Rebasing applies the changes from the branch after the changes from another + +To rebase: +``` +git rebase +``` + +### Exercise 01.01 +In your sandbox project experiment with branch and rebasing: +1. Create some branches +2. Commit different new files to each to the branches +3. Use both merging and rebasing +4. Observer how the git log looks after these changes + +## Conflicts +The merge command shown earlier will fail if there are changes to the same files in both branches. +No fast forward merge and resolve conflicts +``` +git merge +``` +Resolve the conflicts and use `git add` to mark as merged. Committing will apply the merge. + +Rebasing your source branch may also solve or reduce the merge conflicts + + + +## Branching Strategy +There are many complex branching strategies (see Git Flow for example). However, it is best to keep it simple. + +"Trunk" Development: Keep the latest on master + +## Feature branching +- Branch to develop a feature +- Keep features small +- Merge back to master as soon it is ready + +![alt text](feature_branching.svg "The feature branching strategy") + +## Release Branching +- Required only if multiple versions are to be maintain e.g 2.3.n and 3.0.n + diff --git a/08_code_management/02_Remotes_and_GitHub.md b/08_code_management/02_Remotes_and_GitHub.md new file mode 100644 index 0000000..79df587 --- /dev/null +++ b/08_code_management/02_Remotes_and_GitHub.md @@ -0,0 +1,61 @@ +# Remotes and GitHub + +## Git Remotes +- A remote a is repository on a server that is tracked by the local repository +- Git is decentralised and can have multiple remote servers +- Local branches can be configured track remote branches +- Different branches can track different remotes + +Adding a remote: +``` +git remote add +``` +Default remote name is `origin` + +Push a branch to a remote branch +``` +git push --set-upstream +``` + +pushing changes from local branch to remote branch +``` +git push +``` + +pull changes from remote branch to local tracking branch +``` +git pull +``` + +To make you local branch be aware of latest set of remote branches you will need to fetch from the remote + +fetching branches and tags from remote +``` +git fetch +``` +If fetching from remote called `orgin` +``` +git fetch +``` + + +## GitHub +- GitHub is a hosted Git Server at https://github.com +- Free account allow unlimited public and private projects. Though only 3 collaborates are permitted on private projects is restricted + +### Exercise 02.01 +1. Register a at github.com +2. Create a public repository for your project with BSE3 licence +3. Configure the remote and push master +4. Create a private repository for your sandbox +5. Configure the remote and push all branches + +*Note:* You will be creating your repository with BSE3 licence. + +## Cloning a existing Repository +``` +git clone +``` + +### Exercise 02.02 +Create and new project in GitHub and clone it locally diff --git a/08_code_management/03_README_and_Markdown.md b/08_code_management/03_README_and_Markdown.md new file mode 100644 index 0000000..e2253b3 --- /dev/null +++ b/08_code_management/03_README_and_Markdown.md @@ -0,0 +1,86 @@ +# Living Documentation + +## README +- Plain text file traditionally distributed with source to provide documentation on the software +- GitHub will display README files to the root of the project and any directories that contain them +- README files written in markdown (README.md) will be display as formatted HTML in GitHub +- These form part living documentation of the code as it can be update alongside the code the in code repository + +## Markdown +A plain text readable format that can also be converted to HTML or displayed in other rich format + +Headings +```markdown + # Top level heading + + ## Level two heading + + ### Level three heading + +``` + + +Bullets lists and numbered lists +```markdown +- Point 1 +- Another point + +1. The first item +2. Yet other item +``` +- Point 1 +- Another point + +1. The first item +2. Yet other item + +Note: Number list that are non-sequential will be displayed sequentially + +Code blocks +````markdown +```python +def add_one(n): + return n + 1 +``` +```` +```python +def add_one(n): + return n + 1 +``` +Tables +```markdown +|Name of Fruit |Price of Fruit| +|:-------------|-------------:| +|Orange |40p | +|Apple |20p | +|Banana |50p | +``` +|Name of Fruit |Price of Fruit| +|:-------------|-------------:| +|Orange |40p | +|Apple |20p | +|Banana |50p | + +*_Plus more!!!_* + +## Exercise +Create a README file for your assignment providing a description of what the software will be written in markdown. + +## Documentation comments +Many languages have built in support or tools for generating documentation or for provide help in IDEs. + +Python has docstrings + +```python +def add_one(n): + """ + Adds one to a number + :param n: The number one will be added to + :return: the number with one added + """ + return n + 1 +``` + +Doxygen can be used to generate documentation for C and C++ (as well as many other languages) +### Exercise +Experiment with creating Doxygen (http://www.doxygen.nl) comments in C++ \ No newline at end of file diff --git a/08_code_management/04_Pull_Request_and_Forking.md b/08_code_management/04_Pull_Request_and_Forking.md new file mode 100644 index 0000000..6454d6b --- /dev/null +++ b/08_code_management/04_Pull_Request_and_Forking.md @@ -0,0 +1,12 @@ +# Pull Requests and Forking + +## Pull request +- Pull requests are means are developers can collaborate by sharing and reviewing their changes. +- Pull requests can be crate from branches, forks and patches. +- Provide a means of peer reviewing and reviewing feature branches as they merge to master + +## Forks +- Forking allows one to take a copy of project and makes changes with out effecting the original project +- Pull requests can be raise back to the original project + +*Demonstration* \ No newline at end of file diff --git a/08_code_management/05_Issues.md b/08_code_management/05_Issues.md new file mode 100644 index 0000000..9287767 --- /dev/null +++ b/08_code_management/05_Issues.md @@ -0,0 +1,6 @@ +# GitHub Issues + +- Allow one to create a list of task that need to be done to deliver the softare +- Can be used to track features to be delivered +- Can be used to track defects in the software +- Allows other to raise defects and provide feature requests diff --git a/08_code_management/06_Other_GIT_commands.md b/08_code_management/06_Other_GIT_commands.md new file mode 100644 index 0000000..e69de29 diff --git a/08_code_management/feature_branching.svg b/08_code_management/feature_branching.svg new file mode 100644 index 0000000..f10b691 --- /dev/null +++ b/08_code_management/feature_branching.svg @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Merge + + + + + + Master + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + feature/abc + + + + + + feature/efg + + + + + + bug/123 + + + + + + + + \ No newline at end of file diff --git a/09_testing/00_TDD.md b/09_testing/00_TDD.md new file mode 100644 index 0000000..a7c9ede --- /dev/null +++ b/09_testing/00_TDD.md @@ -0,0 +1,72 @@ +# Test Driven Development +## Unit testing +- Testing of units of code, i.e. testing only part/section of the code +- Written by the developer to validate the their code + + +### Unit testing in Python +The _doctest_ approach: +```python +def add_stuff(self, n, m): + """ Pointless function that adds two numbers + + >>> add_stuff(3, 5) + 8 + """ + return n + m + +``` + +To execute the test form the command line: +``` + python -m doctest -v myfunctions.py +``` +Or you can run doctests in PyCharm + +The _unittest_ apporach +```python +import unittest +import myfunctions + +class SomeTest(unittest.TestCase): + + def test_it_adds(self): + self.assertEqual(myfunctions.add_stuff(4, 7), 11) + +``` + +To execute the test form the command line for a single file: +``` + python -m unittest some_tests.py +``` + +To execute the test form the command line for a directory/module : +``` + python -m unittest +``` + + +Or you can run unittests in PyCharm + + +## Developing following TDD + +### The Cycle +- Red - Write a single test and ensure it fails +- Green - Make the test pass +- Refactor - refactor the code and rerun the test +Repeat above + +![alt text](TDD.gif "The Test Driven Development cycle") + +### The Ground rules +1. Write one test a time and follow the cycle +2. Assume the code exists +3. Let the IDE do the work +4. Write only the code required to pass the test + +## Demonstration +Leap year function + +## Exercise +Write a function that can take a String containing Roman numerals and return a the equivalent number. \ No newline at end of file diff --git a/09_testing/01_Mocks.md b/09_testing/01_Mocks.md new file mode 100644 index 0000000..ecab9ae --- /dev/null +++ b/09_testing/01_Mocks.md @@ -0,0 +1,188 @@ +# Mocking +Mocking is the practice of using mock implementation of classes called by the class under test. + +## Why mock +- Separation of Concerns +- External dependencies +- Predictable behaviour + +## Dependency Injection +In Dependency Injection classes do not instantiate the objects they use, they are injected by an injector. +- Supports Separation of concerns +- Supports Modularisation +- Supports mocking in testing + +Ways of achieving +### Constructor injection + +#### In Python +```python +class MyClass(object): + + def __init__(self, injectedObject): + self.injected = injectedObject + + def do_something(self): + self.injected.calculate() +``` +#### in C++ +Interface +```c++ +class InjectionInterface { + public: + virtual int addNumbers(int n, int m) = 0; +}; + +``` +Implementation class +```c++ +class InjectionImplementation: public InjectionInterface{ + public: + int addNumbers(int n, int m){ + return n + m; + } +}; + +``` + +Class injected to: +```c++ +class MyClass { + public: + MyClass(InjectionInterface &dependecy) { + dep = dependecy; + } + + private: + InjectionInterface &dep; +}; +``` +Injection +```c++ +int main() { + InjectionImplementation myDep; + MyClass mc(myDep); +} +``` + +### Method/variable injection +In Python: +```python +class MyClass(object): + + def do_something(self): + self.injected.calculate() + +``` + +A hybrid approach of default instance which may be useful to get started with +```python +class MyClass(object): + + def __init__(self): + self.injected = MyCalculator() + + def do_something(self): + self.injected.calculate() + +``` + +## Mocking in Python + +Example of Python file being tested: +```python +from temperature_sensor import TemperatureSensor + + +class TemperatureTracker: + + def __init__(self) -> None: + self.sensor = TemperatureSensor() + self.start_temp = 0 + + def record_initial_temperature(self): + self.start_temp = self.sensor.check_temperature() + + def find_temperature_change(self): + return self.sensor.check_temperature() - self.start_temp +``` + +## Mocking with Mock class +```python +import unittest +from temperature_tracker import TemperatureTracker +from unittest.mock import Mock + + +class TestWithMock(unittest.TestCase): + + def test_tracks_temperature_change(self): + tracker = TemperatureTracker() + mock_sensor = Mock() + tracker.sensor = mock_sensor + + mock_sensor.configure_mock(**{'check_temperature.return_value': 12}) + + tracker.record_initial_temperature() + mock_sensor.configure_mock(**{'check_temperature.return_value': 22}) + self.assertEqual(10, tracker.find_temperature_change()) + + +if __name__ == '__main__': + unittest.main() + +``` + +## Mock with patch +```python +import unittest +from temperature_tracker import TemperatureTracker +from temperature_sensor import TemperatureSensor +from unittest.mock import patch + +results = [12, 22] + + +class TestWithPatch(unittest.TestCase): + + @patch.object(TemperatureSensor, 'check_temperature', side_effect=results) + def test_tracks_temperature_change(self, mock): + tracker = TemperatureTracker() + tracker.record_initial_temperature() + self.assertEqual(10, tracker.find_temperature_change()) + + +if __name__ == '__main__': + unittest.main() + +``` + +## Using patch with built in function +Python file under test: +```python +import time + + +def whats_the_time(): + return time.time() +``` +Test code: +```python +import unittest +import clock +from unittest.mock import patch + + +class MyTestCase(unittest.TestCase): + + @patch('time.time', return_value=1571871846.8861961) + def test_the_time(self, mock): + self.assertEqual(clock.whats_the_time(), 1571871846.8861961) + + +if __name__ == '__main__': + unittest.main() +``` +## Exercise +Name picker: +Implement a function that can provides a random name from the top 100 list of baby names in the UK in 2018 \ No newline at end of file diff --git a/09_testing/02_CI.md b/09_testing/02_CI.md new file mode 100644 index 0000000..58f09a0 --- /dev/null +++ b/09_testing/02_CI.md @@ -0,0 +1,39 @@ +# Continuous Integration +- The practice of frequent and small merges of features +- Supported by automated building, testing and analysis of all branches +- Supports collaboration of large number of developers + +## Travis CI +- Online Continuous integration tool +- Integrates with Github +- Very easy to set-up. Minimal configuration files with defaults, which can over ridden if the code is built differently + +A start Travis `.travis.yml` file for Python 3.8 using unittest module: +```yaml +language: python +python: + - 3.8 +script: + - python -m unittest discover test +``` + +Minimal Travis `.travis.yml` file for C++: +```yaml +language: cpp +``` + +## Demonstration +Setting up travis for a Python project in GitHub + +## Exercise 02.01 +1. Create a .travis.yml file in your sandbox GitHub project +2. Sign-in to https://travis-ci.org/ with your GitHub credentials +3. Configure you project +4. Create a branch with a add a new unit that does not pass +5. Create pull request +6. Ensure you can see icon in the +7. Repeat steps 1 to 3 for your main project + + + + diff --git a/09_testing/03_Code_Coverge.md b/09_testing/03_Code_Coverge.md new file mode 100644 index 0000000..45ff8e2 --- /dev/null +++ b/09_testing/03_Code_Coverge.md @@ -0,0 +1,73 @@ +# Code Coverage + +## What is Code Coverage + +- Measures which parts of a codebase are covered by automated tests +- Can be used to identify untested code +- Useful to ensure standards of testing are maintained throughout a project + +## Types of code coverage + +There are several ways to measure code coverage + +- Function coverage: how many of the functions defined have been called. +- Statement coverage: how many of the statements in the program have been executed. +- Branches coverage: how many of the branches of the control structures (if statements for instance) have been executed. +- Condition coverage: how many of the boolean sub-expressions have been tested for a true and a false value. +- Line coverage: how many of lines of source code have been tested. + +## Gotchas +- Code coverage is not a panacea +- High Coverage alone is not a guarantee of well tested, maintainable code +- 100% is often unattainable and may well not be worth the level of effort required - better to maintain a moderate but consistent level throughout the application +- It is entirely possible to write bad unit tests which have high code coverage - code coverage only tells you that the code was executed, not that it was well tested. + +## Code Coverage in Python - coverage.py +### Install It +First, install Coverage.py: + +``` +pip install coverage +``` + +### Gather Data +Run your test suite, but replace python blah blah with coverage run blah blah + +For example: + +``` +coverage run Test.py +``` + +or, to collect branch coverage data + +``` +coverage run --branch Test.py +``` + +Ideally your tests should be outside of your source directory, so that you don't have to configure excludes or package them. It's much neater that way. + +### Print Coverage Reports +#### Command-Line Report +To print a quick command line report, maximize your command prompt and type: + +``` +coverage report -m +``` +#### HTML Report +To print a fancier HTML report: + +``` +coverage html +open html_cov/index.html +``` + + +## Demo - Person.py + +## Exercise + +Now try running the test suite you wrote earlier using Coverage. + +Since you have been using TDD, you should find high code coverage, +try adding an extra untested method or an untested branch to your code diff --git a/09_testing/04_Static_Analysis.md b/09_testing/04_Static_Analysis.md new file mode 100644 index 0000000..e69de29 diff --git a/09_testing/05_Functional_Testing.md b/09_testing/05_Functional_Testing.md new file mode 100644 index 0000000..748a248 --- /dev/null +++ b/09_testing/05_Functional_Testing.md @@ -0,0 +1,197 @@ +# Function Test + +## Behaviour Driven Development (BDD) + +- Builds on the concepts of Test Driven Development (TDD) +- Outside looking in - What is does not how it does it +- Uses Structured natural language to capture specification +- Tests are constructed from the specifications +- Implement one feature at a time +- Can be used to measure development progress +- The specification forms a living documentation of what the software does + +TODO: Show chart + +### Some Examples of Specification Languages + +"It Should" + +```gherkin +Alarm clock + should display the time + should wake up owner + +``` + +Gherkin + +```gherkin +Given Alarm set +When when time of Alarm reached +Then Alarm rings + +``` + +## Gherkin in more detail +### Basic Format +```gherkin +Feature: Here is free form description of the feature that can be several lines long. + +Scenario: This would be a description of one scenario +Given precondition +When action +Then expected outcome + +Scenario: A description of another scenario + +``` + +### And and But: +```gherkin +Scenario: A more complex scenario +Given a precondition +And another precondition +When action +And another action +Then expected outcome +And another expected outcome +But a negative expected outcome +``` + +### An Example +```gherkin +Scenario: Valid login +Given a registered user +When user supplies correct login details +Then user is logged in + +Scenario: Invalid password +Given a registered user +When user supplies incorrect password +Then user is shown "Username/Password incorrect" error message +And user not logged in + +Scenario: Invalid username +Given a registered user +When user supplies incorrect username +Then user is shown "Username/Password incorrect" error message +And user not logged in + +``` + +### Using Example tables +```gherkin +Feature: Fruit Multi-purchase discounts. Apples are €0.50 each for up to 4 (inclusive), then €0.45 each, for 10 or over they are €0.40. Oranges are €0.80 each for the first 12, then €0.75. + +Scenario: Shopper buys single type of fruit. +Given buying +When shopper picks +Then total price comes to € + +Examples: + | fruit | number | price | + | apple | 1 | 0.50 | + | apple | 3 | 1.50 | + | apple | 4 | 2.00 | + | apple | 5 | 2.25 | + | apple | 8 | 3.60 | + | apple | 10 | 4.00 | + | apple | 12 | 4.80 | + | orange | 1 | 0.80 | + | orange | 6 | 4.80 | + | orange | 12 | 9.96 | + | orange | 13 | 9.75 | + +``` +TODO: Add reading suggestion Specification by Example + +### Imperative and Declarative +```gherkin +Imperative +Given … +When User arrives a journal land clicks on login field +Then User is redirected to login screen +When enters text "myUserId" in field "Username" +And enters text "myPassw0rd" in field "Password" +And user clicks "Login“ +Then … + +Declarative +When user supplies correct login details + +``` + +### Sensible Defaults +```gherkin +Scenario: “Default” user logs in +Given a registered user +When user supplies correct login details +Then user is logged in + +Scenario: User with expired password +Given a registered user +And password has expired +When user supplies correct login details +Then user is shown change password screen +And user not logged in + +``` + +## BDD tools +- Parse specification and executes test code +- Typically can create code place holders from specification +- Reporting of test results + +Many tools are available that support many different test written in many different lanaguages. We will look a Behave with Python. + +## Behave +Behave is a BDD framework for Python (https://behave.readthedocs.io). + +### Demonstration +An example of using Behave. + +### Install Behave + +``` +pip install behave +``` +### Using behave +Now make a directory called “features”. In that directory create a file called “tutorial.feature” containing: + +```gherkin +Feature: showing off behave + + Scenario: run a simple test + Given we have behave installed + When we implement a test + Then behave will test it for us! +``` + +Make a new directory called “features/steps”. In that directory create a file called “tutorial.py” containing: + +```python +from behave import * + +@given('we have behave installed') +def step_impl(context): + pass + +@when('we implement a test') +def step_impl(context): + assert True is not False + +@then('behave will test it for us!') +def step_impl(context): + assert context.failed is False +``` + +Run behave: + +``` +behave +``` + +## Exercise +Create and execute a test against your software project using Behave + + diff --git a/09_testing/TDD.gif b/09_testing/TDD.gif new file mode 100644 index 0000000..a32af85 Binary files /dev/null and b/09_testing/TDD.gif differ