NML Says

Open Source Development, Intro

References for this Lesson

UML Diagrams

Brasseur, V. M. Forge Your Future with Open Source 1st ed., Pragmatic Bookshelf, 2018

Beck, Kent. Test-driven Development by Example. Vaseem: Addison-Wesley, 2002

Asserts in JS

Asserts in Python

Intro to Course …

From the MID

Indicative Content

Tools for team software development: version control (using subversion, mercurial or git, for example); bug tracking; testing frameworks; group communication tools;

Legal issues: the range of Free and Open-Source licenses; example legal cases regarding open-source software;

Open-source development: documentation of code and software, standards, cross-platform development and packaging of software, writing multilingual software.

Teaching and Learning

Learning will be facilitated through a variety of methods, which may include lectures, seminars, lab, workshops, online activities and group work. Students are expected to engage in both class and online activities and discussions. This module also requires students to participate in additional guided reading and self-directed study to reinforce the learning gained from timetabled sessions. Formative assessment will be used to prepare students for summative assessment and give students an early indication of their progress towards the course intended learning outcomes.

Recapping Git for Our Context

Writing software that you intend to share, must be under a Version Control System, a VCS. In this context this means git. Why git? Because it is the best.

Sharing with someone who is not local? You must use one of the repository hosting services. The most well known are https://codeberg.org, https://gitlab.com, and https://github.com

To get started with a new project we have, by trial and error, found a useful workflow:

  1. Decide on a name for your project, use the same naming rules that you would use for a variable name in your favorite programming language. Choose a durable name.
  2. Log in to your account on https://codeberg.org, https://gitlab.com, or https://github.com
  3. Create an empty, or almost empty non private repo with your chosen name.
  4. Navigate to your projects directory
  5. On the CLI do
    1
    2
    3
    
    git clone https://codeberg.org/arosano/Examplar0
    # or
    git clone https://github.com/nmliba/Examplar1
    
  6. Start working
  7. Commit and test often
  8. Push when tests indicate you have something to share

For this we have created a couple of exemplary near empty projects. Read on below. The repo being non private means that others may clone your repo. The partners you choose for collaborating on the project must do that. Others may clone it. Your collaborators should probably be allowed the necessary permissions to push changes to the repo.

Choose a good name. Once the repo is in the open you should not change it. Have you not chosen wisely, you do not rename, but archive the repo ie make read-only, and then create a new repo with a better name based on a branch of the project. Then you must of course inform stakeholders of the change.

  1. Checkout this repo: Codeberg version, or
  2. this Github version
  3. Read the README
  4. Check the .gitignore
  5. Check the LICENSE

The .gitignore is a long file which covers most eventualities. It is probably way over what we need in our coursework and exercises. We chose a python version for the Codeberg repo, and a node version for the Github version.

Re the licensing, there is the mentioned file with the full license. Int individual code files we may do something like the following:

1
2
3
4
5
6
7
8
'''
	python0.py

	Copyright (c) Niels Müller Larsen
	Licensed under the MIT license: https://opensource.org/license/mit
'''

# code starts here

or

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/*
	js0.js

	Copyright 2025 Niels Müller Larsen

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

		http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

// code starts here

Intro to Testing

Testing, Background

Computer systems must be tested. Hardware, or as in our context, systems, software. This is about testing.

There are several test concept in system engineering, devlopment, but they all have in common that the test should ascertain that the software does what it is supposed to do. This begs the question: what is it supposed to do?

What is it supposed to do? You will find the answer to that in the specs. This means that there is a proces preceding writing code. The developer gets a system specification explaining in detail what the requirements are. There are usually also a number of use cases involved. Use cases are part of user scenarios.

User Scenarios aka Use Cases

Use cases are part of UML, Unified Modelling Language6

Use Cases 1

Use Cases 2

Use Cases 3

The high level use case:

Figure 19.1. High Abstraction

High Abstraction

Use Cases 4

A use case could be verbose:

Use Cases 5

Use Cases 6

A payment system example:

Figure 19.2. Payments

Payments

Aspects of Testing

Let us look at some focal points of testing with reference to SitePoint

We generally distinguish between different levels of software testing:

Unit testing
testing specific lines of code, functions, etc. Integration testing
tests integration between units
System testing
tests the entire system.
Acceptance testing
checks the compliance with business goals as expressed in specs.

Generally speaking the former two are done by the developers, while the latter two involve testers and users.

Test Strategy

Apart from the professional pride of the developer, if that still exists, there are some overall limits that should be considered for a strategy. They include:

Risks
What are the business consequences if a bug were to affect this component?
Time
How soon must you deliver? Do you have a deadline?
Budget
How much money are you willing to spend on testing?

Risk, time, and budget are factors not just in software development, but are general project management topics.

Unit Test Characteristics

The main characteristics of unit tests are

Speed
Unit tests are mostly executed automatically, which means they must be fast. Slow unit tests are more likely to be skipped by developers because they don’t provide instant feedback.
Isolation
Unit tests are standalone by definition. Each test tests one thing. They don’t depend on anything external like a file or a network resource, if they depend on any other function, they are by definition an integration test.
Repeatable
Unit tests are executed repeatedly. They are contained in a program that you run.
Reliable
Unit tests themselves are tested before use. They may fail only if there’s a bug in the system being tested. They must be independent of environment, or the order of execution.
Naming
Proper naming is as essential in tests as in software development generally. The name of a test should be clear and unambiguous.

The Howto of Testing

Overview

Computer systems must be tested. Hardware, or as in our context, systems, i.e. software. This section is about testing.

Wikipedia has the following about test-driven development. It cites for this and for being the father of TDD. The quote has been cleaned from Wikipedia internal links. The Wikipedia article is very well referenced. Use the link above.

_1. Add a test
The adding of a new feature begins by writing a test that passes if and only if the feature’s specifications are met. The developer can discover these specifications by asking about use cases and user stories. A key benefit of test-driven development is that it makes the developer focus on requirements before writing code. This is in contrast with the usual practice, where unit tests are only written after code.
_2. Run all tests. The new test should fail for expected reasons
This shows that new code is actually needed for the desired feature. It validates that the test harness is working correctly. It rules out the possibility that the new test is flawed and will always pass.
_3. Write the simplest code that passes the new test
Inelegant or hard code is acceptable, as long as it passes the test. The code will be honed anyway in Step 5. No code should be added beyond the tested functionality.
_4. All tests should now pass
If any fail, the new code must be revised until they pass. This ensures the new code meets the test requirements and does not break existing features.
_5. Refactor as needed, using tests after each refactor
to ensure that functionality is preserved
Code is refactored for readability and maintainability. In particular, hard-coded test data should be removed. Running the test suite after each refactor helps ensure that no existing functionality is broken.

Examples of refactoring

  • moving code to where it most logically belongs
  • removing duplicate code
  • making names self-documenting
  • splitting methods into smaller pieces
  • re-arranging inheritance hierarchies
Repeat
The cycle above is repeated for each new piece of functionality. Tests should be small and incremental, and commits made often. That way, if new code fails some tests, the programmer can simply undo or revert rather than debug excessively. When using external libraries, it is important not to write tests that are so small as to effectively test merely the library itself, unless there is some reason to believe that the library is buggy or not feature-rich enough to serve all the needs of the software under development.

Testing Pattern

SitePoint advocates what they call the AAA pattern.

The Arrange, Act and Assert pattern is a common strategy used to write and organize unit tests. It works in the following way:

  • During the Arrange phase, all the objects and variables needed for the test are set.
  • Next, during the Act phase, the function/method/class under test is called.
  • In the end, during the Assert phase, we verify the outcome of the test.

This strategy provides a clean approach to organizing unit tests by separating all the main parts of a test: setup, execution and verification. Plus, unit tests are easier to read, because they all follow the same structure.

Now, let us proceed in more concrete direction.

Code

Now take your pick, choose one of the following

Unit Testing JavaScript

or

Unit Testing Python

and return to here when you are done.

Exercises

The rules for submitting solutions may be found in the README

For the following three exercises create one project directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
exercises_DS0
|-- exDS00
|   |-- fizzbuzz.py
|   `-- testsuite.py
|-- exDS01
|   |-- palindromy.py
|   `-- testsuite.py
`-- exDS02
    |-- primes.py
    `-- testsuite.py

and make the repo for submission in exercises_DS0. You may solve them in either JavaScript or Python. Your choice!

Exercise OSD.0.0

Given the following specification, first write at least three tests into the file testsuite.py for the functions the program to fulfill the specification need to contain. Then write the program. Remember to test frequently. The tests help you.

Specification

Write a program that prints the numbers from 1 through 100 on the console with three exceptions:

  1. If 3 divides the number write “Fizz” instead of the number
  2. If 5 divides the number write “Buzz” instead of the number
  3. If both 3 and 5 divide the number write “fizzBuzz”

Sample output:

1
2
3
4
5
6
7
8
1
2
Fizz
4
Buzz
Fizz
7
...

Exercise OSD.0.1

First a couple of definitions:

Definition 1:

Re Wikipedia:

A palindrome is a word, number, phrase, or other sequence of symbols that reads the same backwards as forwards, such as madam or racecar, the date “22/02/2022” and the sentence: “A man, a plan, a canal – Panama”.

Definition 2:

A palindrome is a string whose first and last characters are the same, and the strings between the first and the last characters are a palindrome.

The first definition implies that spaces and punctuation is ignored.

Given the following specification, first write at least three tests into the file testsuite.py for the functions the program to fulfill the specification need to contain. Then write the program. Remember to test frequently. The tests help you.

Specification

Write a function that takes a string as input, and returns “true” if the string is a palindrome, and “false” if it is not.

Write a program that includes the codefile with the function, and tests words or phrases for “palindromy.”

Exercise OSD.0.2

A definition:

Re Wikipedia:

A prime number (or a prime) is a natural number greater than 1 that is not a product of two smaller natural numbers. A natural number greater than 1 that is not prime is called a composite number.

Given the following specification, first write at least three tests into the file testsuite.py for the functions the program to fulfill the specification need to contain. Then write the program. Remember to test frequently. The tests help you.

Specification

Write a function that takes a natural number, a positive integer, as input, and returns “true” if the input is prime, and “false” if it is not.

Write a program that includes the codefile with the function, and tests the input for primality.

Test the function with, among others, the integers 9007199254740991, and 9007195909437503.