Category Archives: CS@Worcester

Week 18B – C Testing

For this week, I wanted to look at how different languages handle test cases, and I’ll continue with one I’m not the most familiar with, C! I’ve worked in small amount of C in classes at Worcester State, but have little experience outside of that. I feel like this is a good topic to discuss as knowing how other programming languages handle unit testing would be a great way to expand my knowledge when it comes to furthering my understanding of it within Java.

If you haven’t already read my other blog post on Python testing, feel free to read it right here!

For learning about unit testing in C, I consulted this article on the subject: https://interrupt.memfault.com/blog/unit-testing-basics

It seems like unit testing in C is a lot more barebones compared to Java, which in my experience utilizing C, makes sense for the language. A lot of features primarily used in Java, like object-oriented structures aren’t available in C (to my understanding, could totally be wrong).

For one major aspect, there seems to be only one assertion command in C, just simply “assert”. Theres no assertTrue, assertFalse, assertThrows, or assertEquals, just simply “assert”. And from the example given below:

#include <assert.h>

// In my_sum.c
int my_sum(int a, int b) {
  return a + b;
}

// In test_my_sum.c
int main(int argc, char *argv[]) {
  assert(2 == my_sum(1, 1));
  assert(-2 == my_sum(-1, -1));
  assert(0 == my_sum(0, 0));
  // ...
  return(0);
}

It seems the “assert” function comes from the <assert.h> library, much like the JUnit librarys used in Java. But more importantly, it seems that “assert” is the equivalent of “assertEquals”.

It also seems like Unit Testing in C is best implemented with tools outside of a compiler for C. The ones mentioned in the article in specific were CppUTest, Unity, and Google Test. For the rest of the article, the use examples using CppUTest. It was interesting to hear one of the options being called Unity, which is the name of a game engine, which, while not written in C, is written in a mixture of C# and C++, which are both offshoots of C. Makes me wonder how testing in a gaming engine works, perhaps it’s something to look at in a future blog post, hint hint, wink wink.

CppUTest seems to implement the same SetUp() and Teardown() functions that JUnit can employ, which is really good, as these methods are important for testing multiple methods. It also seems to have more then just an Equals assertion, even though the example used is another equals example.

This gets me more interested in C, as I have been told understanding C allows you to understand other languages much more clearly. Perhaps I’ll take a deeper dive some day, who knows! Until next time, my readers~!

From the blog CS@Worcester – You&#039;re Telling Me A Shrimp Wrote This Code?! by tempurashrimple and used with permission of the author. All other rights reserved by the author.

Week 18A – Python Testing

For this week, I wanted to look at how different languages handle test cases, and I’ll begin with the one I’m the most familiar with, Python! I’ve worked with Python in small amounts in the past, and have an understanding a lot of it’s syntaxes are similar to java’s, albeit simpler. I feel like this is a good topic to discuss as knowing how other programming languages handle unit testing would be a great way to expand my knowledge when it comes to furthering my understanding of it within Java.

For this, I’ll be looking at the official page for unittest on Python’s website, here:

https://docs.python.org/3/library/unittest.html

Right off the bat, I’m really interested in the fact that unittest is actually based directly off of JUnit! Which means a lot of the syntax, formatting, and framework is quite similar, just modified to fit the mold of Python.

Looking at the snippet they gave as an example…

import unittest

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)

if __name__ == '__main__':
    unittest.main()

In this, it seems the way you define test blocks is by having a class with (unittest.testcase) and then doing “def” to define each test case.

Even the assertions are the same and written near identically, as the first three use assertEqual, which is identical to javas assertEquals, minus the s, and assertTrue and assertFalse, which are also identical to their java counterparts. assertRaises, which is used in the third test, seems to be Python’s equivalent to assertThrows, however, it seems to be a bit different in comparison. assertRaises seems to identify a specific kind of exception being raised, whereas assertThrows would just identify any exception in general.

The last line also is a block of code that allows an easy way to run all the tests, so when you run unittest.main() in a command line, it will automatically run all the tests and display the results.

There also seems to be a whole bunch of different command line options to display results and modify the ways in which its run. As an example, theres “-v”, which stands for verbosity, much like the bash command, which shows the results of each individual test being run, like below:

test_isupper (__main__.TestStringMethods.test_isupper) ... ok
test_split (__main__.TestStringMethods.test_split) ... ok
test_upper (__main__.TestStringMethods.test_upper) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

It seems extremely interesting and makes me want to learn more Python, which would definitely help me in my career in all sorts of ways! Next blog we will be looking at how unit testing works in C. Until then!

From the blog CS@Worcester – You&#039;re Telling Me A Shrimp Wrote This Code?! by tempurashrimple and used with permission of the author. All other rights reserved by the author.

Behavior Driven Development

Behavior Driven Development ( BDD ) is a test practice that makes sure there is good quality by automating test before or during system behavior specification. BDD test focuses on facing scenarios that describe the behavior of a story, feature, or capability from a user’s perspective. When the tests are automated they make sure that the system constantly meets the required behavior.

The Behavior Driven Development Process

The BDD process has three phases to it. The discovery phase, formulation phase, and the automation phase.

1.) Discover phase: This phase is where the user creates the initial acceptance agenda for the feature. This phase is usually done in a collaborative manor, each team member is contributing.

2.) Formulation phase: This phase is where the acceptance agenda sets into detailed acceptance tests, as the backlog item gets closer to implementation. This phase also incorporates specific examples of the behavior.

3.) Automation phase: This phase is where automation tests are automated to run constantly. This is to make sure that the new system supports the new behavior.

Benefits of Behavior Driven Development

1.) Early detection of errors / defects: When you automate tests in the early stages of development process, you can identify and address the issues. BDD allows for the early detection of defects.

2.) Faster Flow and Time: when using BDD, you can reduce the errors, rework, and replan. BDD accelerates the flow of the development process. Developers can produce features / products faster and more efficiently.

3.) Stronger Test Coverage: BDD allows for a more comprehensive test coverage that focuses on the user behavior and scenarios. Both common and edge cases are tested as well.

4.) Clear understanding: BDD can be plain and clear to understand, because specific scenarios are used to describe the behavior from a user’s point of view. This helps the development to fully understand the requirements and whats going on.

Why I chose this resource

I chose this article ” Behavior Driven Development” because it provided a detail look of a very important test method that goes in conjunction with the technical and business aspect of testing. Understanding BDD is important in today’s society of software development, for giving an efficient and more user friendly user products.

Personal Reflection

This article increased my understanding of BDD and the use of it in software development. I learned a lot about how BDD strengthens collaboration and communication between the business side of things and the technical side of things. This helps to ensure that user’s expectations and requirements are met. The new found knowledge will be extremely valuable in my future endeavors because I will incorporate this method in my future projects. This will help to improve the development process and product efficiency and quality. Also, by using BDD I can make sure that all requirements and specifications are met.

The full article is here: https://scaledagileframework.com/behavior-driven-development/

From the blog CS@Worcester – In&#039;s and Out&#039;s of Software Testing by Jaylon Brodie and used with permission of the author. All other rights reserved by the author.

Static Testing vs. Dynamic Testing

Testing in software development is important because it helps to deliver efficient and user friendly products to the end user. It also provides the developers with a chance to improve upon the product. Static and Dynamic testing are two important techniques used in software development.

Static Testing

Static Testing has various names like Verification Testing, Non-execution Testing, etc. This testing technique is used to identify defects in software without actually executing the code. This method usually includes manual and automated evaluation of the software and the code. Developers use this method usually in the beginning stages of the development process to catch issues early on, which will also lead to be easier and cheap to fix. This method focuses on reviewing the test cases, test scripts, test plans, and source code.

Static Testing Techniques

1.) Informal Reviews: Developers review each of the documents and give feedback

2.) Walkthroughs: Someone presents the product to the team and someone else takes notes.

3.) Technical Reviews / Code Reviews: review the technical specifications and the source code to make sure everything meets the requirements and standards.

4.) Inspection: Check for defects. Developers usually review the process with a checklist to help identify and record for defects.

Dynamic Testing

Dynamic Testing is a technique that analyzes the dynamic behavior of the code by actually executing it. This method makes sure to check that the software functions correctly and that there are no underlying issues / conditions. Sometimes developers use this method in conjunction with black box or white box testing to provide more realistic results.

Dynamic Testing Techniques

1.) White Box Testing: Examines the internal code structure. You need to actually have the internal code (source code)

2.) Black Box Testing: Checks the functionality without the actual internal code (source code) .

Benefits of both Static Testing and Dynamic Testing

1.) Early detection of defects

2.) Cost efficient

3.) Showcases runtime errors

4.) Reliability

Why I picked this Resource

I chose the article “Static Testing vs. Dynamic Testing” because this article gave me a more detailed and in depth look between two very important testing methods that are currently being used in todays society. It is very important to understand these two testing methods in the software development process because they can deliver efficient and user friendly products to the end user. This article also aligns with what we have learned in the course, making it relevant to talk about and to understand.

Personal Reflection

This article deepened my understanding of static and dynamic testing. I was able to learn a lot about these two testing methods that I did not know, even the many benefits that each method has. Knowing how crucial these two methods are in the software development process and what I know now, this knowledge will help me on my future endeavors when approaching new projects in regards to testing .

The full article is here: https://www.geeksforgeeks.org/difference-between-static-and-dynamic-testing/

From the blog CS@Worcester – In&#039;s and Out&#039;s of Software Testing by Jaylon Brodie and used with permission of the author. All other rights reserved by the author.

Test Doubles

Test doubles are a very important tool in software testing. Test doubles allow for users to break off a portion of their code to test specific parts and functions. This helps because users can do this without depending on the other factors within their code. Test doubles are substitutes, they copy the behavior of real objects. This helps to make sure that the tests remain structured and efficient.

Overview of Test Doubles

For this blog post, I chose the Article “Test Doubles: Mocks, Stubs, and Fakes Explained” by Martin Fowler. The article talks a lot about the overview of the different types of test doubles, their roles, and how they can be used in testing.

Types of Test Doubles

1.) Dummy: A dummy object is required for the creation of another object required in the code. Dummy objects will never be used in the test, they are simply like place holders to satisfy the code and its requirements.

2.) Fake: A fake is an object that will always have the same return value. This object is useful for testing certain scenarios, like a user that is logged in or in a consistent database response. They are simple implementations that are not that suitable for production but are good for testing.

3.) Stub: A stub will provided predetermined responses to method calls. Stubs usually imitate the behavior of external components like databases or web services.

4.) Spy: A spy will record information about the interactions with the object being under tests. This helps verify interactions and make sure there is the correct behavior in method calls.

5.) Mock: A mock can be a more advanced test double that will allow for dynamic behavior based on the test scenario. They verify interactions and can change behavior based on conditions. They are useful for ensuring that certain methods are called with specific parameters during the test.

Benefits of Using a Test Double

1.) Early detection of errors/issues: Using Test Doubles will help the users to find any issues within the code. This helps with reducing the risk of defects in production

2.) Cost Efficiency: Using Test Doubles will significantly help to reduce the costs that will come with fixing the issues later in the development process.

Why I Picked this Resource

I chose this resource for the blog post because it provided an in depth overview of the various types of test doubles and their specific role within testing. This article’s contents had some similarities of what we discussed in the class, making it relevant and valuable.

Personal Reflection

This article not only increased my understanding on the topic of Test Doubles, but it also showed my how unique and important each one can be in regards to testing. I also learned the various benefits of these test doubles, so when I choose one in my future endeavors I will know which one will benefit me the most.

In my future endeavors, I plan on using what I have learned about these Test Doubles objects by implementing them on future projects. This new found knowledge will help me to make better decisions in the future and will also improve the quality of my work.

The full Article is here: https://ahmadgsufi.medium.com/test-doubles-understanding-the-different-types-and-their-role-in-testing-67cbf71ea252

From the blog CS@Worcester – In&#039;s and Out&#039;s of Software Testing by Jaylon Brodie and used with permission of the author. All other rights reserved by the author.

CS 448-01 Team 3 Sprint 3 Retrospective (5/7)

Following the very close end to our 3rd and last sprint, I feel like we really put in the effort to finish AddInventoryFrontend. As a team, we completed all of the issues that we were assigned as a team and meet up together for many in-person meetings in order to finally finish up some loose-ends.

One of the biggest things that we finished from last sprint was that we were to got AddInventoryFrontend working. Last sprint was very difficult because the code that we were working on was messy and we had to change a few different approaches to the Frontend since our original approach to create a wireframe which would eventually become the UI did not come together. For this sprint, we had updated our code to be able to finally string together the Frontend with the Backend, like changing around our directory, adding in key files to run the Frontend, and then test through trial and error our Frontend. We used our current wireframe in order to build our Frontend to what we ended up with.

For AddInventoryFrontend, I had worked on updating the Documentation of AddInventoryFrontend since I wanted to be able to contribute more in this sprint. When I looked at the documentation in its original state, I was dumbfounded to find that there were almost nothing there to begin with. It must have looked liked a template since it specified that the linter being used was called test.sh instead of lint.sh. Because everyone on my team was doing so much work on the Frontend and its functionality, I wanted to be able to contribute more as a member of the team, so I decided to modify the documentation so that it would reflect the changes that we made as as a team.

Unfortunately, we were unable to completely fix some issue that we had with our Frontend before the end of the sprint. Our Frontend works great and loads properly now that we have fixed it. If we had another sprint left before the end of the semester, we would have worked on optimizing our Frontend so that the button could work so that you can add and remove units of food from the inventory, and also keep track of how much food is in the inventory through a viewable parameter that would check in the database for the inventory amount. With that being said, any issues that we had with AddInventoryFrontend will have to be resolved next year.

As a member of our team, I definitely could work on trying to practicing some code so that I would be able to make changes that they made with the Frontend. The Frontend was not impossible for me to read since I have played around with HTML before, but I was still trying to figure out all the formatting for our Frontend so I took a good look at our code. I could tell that at the very least that we did our best with creating the Frontend with the little time that we had following our previous sprint, but I would like to not forget about the things we did as a team to create our Frontend. I think that I better understand how AddInventoryFrontend works because I did run the environment on my own. For our presentation, I really hope that we can talk more about how we got our Frontend to work rather than just listing out the issues that we did in our sprint.

From the blog CS@Worcester – Elias&#039; Blog by Elias Boone and used with permission of the author. All other rights reserved by the author.

CS 448-01 Team 3: Sprint 2 Retrospective (4/4)

With the second sprint, we had so much trouble with our sprint until near the end of the sprint. To elaborate on what went wrong, I would like to start out with what we were planning from the very start, as this will be very important for what we will be doing for the next sprint.

While our last sprint, we split between meeting remotely and meeting in-person, we finally decided that it would be better for us to meet in-person. We also came up with a wireframe that we decided to use as our template to create our framework for AddInventoryFrontend (https://gitlab.com/LibreFoodPantry/client-solutions/theas-pantry/documentation/-/blob/main/Developer/Wireframes.md). Since we already had AddInventoryBackend working as intended with the proper testing IDs being used as a way to test our code for the Backend, we only just needed to create AddInventoryFrontend so that we can try to put a frame over all the work that was done with the Backend from last year. At the very least, we knew exactly how we wanted to build our front-end.

On the contrary to how we finally have a plan for our Frontend, I was having lots of trouble with trying to build the Frontend. Since I had lots of trouble with some of the issues that we did, I instead decided to focus on redoing some of the issues we had from last sprint (https://gitlab.com/LibreFoodPantry/client-solutions/theas-pantry/inventorysystem/addinventoryfrontend/-/issues/36). At the very least, I could at least contribute a little bit to our sprint, knowing the tasks that we were unable to completely finish.

What we as a team learned from sprint 2 was that we learned about using Vue, a Javascript framework that we would use to help build our Frontend. While we were not able to get the entire page running, we added a functionality to be able to add a button to our Frontend, just as we intended when we were following our wireframe example from earlier. Once we had explored our options to how we would build our Frontend, we decided to use a new wireframe that my teammate would create for our team to follow along with.

The things I could do improve on as an individual is that I need to speak out more with my team about the issues that may have, let it be related to work or anything other. I had trouble with this sprint because I was not great with programming with HTML and Javascript, and I felt like that was really hindering my performance as a team member. I did my best with trying to get help with working on the sprint, and when that was not working out well for me, I consulted my search engines instead. As someone who was much better with AddInventoryBackend, working with the Frontend was not my strength as shown in this sprint. I was confused with what wireframe we were using for the sprint until the end of the sprint when we had a semi-functioning Frontend that we were going to tweak in our next sprint. For the next sprint, I am hoping that I can get to do anything that is not too technical like directly running the Frontend, and I hope that then next sprint will be where our team will be able to get a working Frontend by the end of next sprint.

From the blog CS@Worcester – Elias&#039; Blog by Elias Boone and used with permission of the author. All other rights reserved by the author.

CS 448-01 Team 3: Sprint 1 Retrospective (2/22)

This beginning of the sprint was a very weird sprint, but me and my team managed to make it through without much trouble.

Seeing how the directory for AddInventoryBackend works, it was easy enough to move the files from their VSCode locations to the directory paths that GitPod uses to be able to use the important files. For this sprint in general, we just needed to move the Linters to the correct directories and then try to run them as best as possible. For the Linters that did not work, we either replaced them with other Linters that were accessible through GitPod, or we just removed them. Since we only needed specific Linters to use for our project, our team were able to confirm that we had a sufficient amount of Linters needed thanks in part to consulting our product owner. Also since I used GitLab, creating issues and labeling them were not too difficult either as we were familiar with managing our issue boards, especially since we learned about workflows in a previous class about Software Process Management.

What did not work well was that I was having a very hard time with trying to use GitLab and GitPod, because I had never directly worked on issues before, making it more difficult for me to fully understand how to utilize my environment until near the end of the sprint. I had made myself a note for the next sprint to remember what I have done for this sprint and what else I had to do for next sprint, because I am very mistake-prone when working on a new IDE. GitPod’s changes are new and more convenient, but as someone who has used other IDEs such as Visual Studio Code and Eclipse to name a few, this was a completely new environment that was very unfamiliar to me. While I did make a few notable mistakes like not understanding how to create merge requests or which tags to use, my team guided me to learn how to be able to make those changes by myself after lots of practice.

As a team, we were really prioritizing meeting up together as necessary as possible. We considered using Discord as a means of having our virtual calls since that is where we were going to communicate and do our stand-ups anyways. However, we found that meeting up in-person was much better for us as working on a sprint by call is not consistent with us since joining a Discord call is too inconvenient and takes up too much time. Like I said before, managing GitLab was not too difficult since we all have experience with Scrum from our previous class. I think the best part about our team is that we are very open to helping each other whenever we were stuck on any issues relating to tasks like with the Linters.

For my individual work in the sprint I had done a couple issues to start out with the sprint. I moved the shell script commands from the /Commands bin to /Bin since that is how we were going to organize our shell-scripts like our lint.sh script (https://gitlab.com/LibreFoodPantry/client-solutions/theas-pantry/inventorysystem/addinventoryfrontend/-/issues/32). Another task I did was very similar to the first one, except I am instead moving the Docker files to specific directories in GitPod (https://gitlab.com/LibreFoodPantry/client-solutions/theas-pantry/inventorysystem/addinventoryfrontend/-/issues/33). The Linter task that I did was to add AlexJS to GitPod so then we can utilize a new Linter to help with checking our code for our project (https://gitlab.com/LibreFoodPantry/client-solutions/theas-pantry/inventorysystem/gitlab-profile/-/issues/83). I did all of those tasks before I would verify to make sure that our entire repository was in the correct state (https://gitlab.com/LibreFoodPantry/client-solutions/theas-pantry/inventorysystem/addinventoryfrontend/-/issues/35). Overall, I think that I am doing good so far individually with the sprints. The one thing I need to work on as a team member is speaking out whenever I need help or so I can find something in particular to do in the sprint since it is not just my team who has to contribute to our work. I am hoping for this next sprint, I can get a specific issue that I can work on to contribute using my skills that I have learned from my previous classes.

From the blog CS@Worcester – Elias&#039; Blog by Elias Boone and used with permission of the author. All other rights reserved by the author.

Apprenticeship Patters Chapter 2 (Emptying the Cup)

For this week, I read the Apprenticeship pattern “Emptying the Cup”, the second chapter of the Apprenticeship Patterns. I found this pattern to be very interesting since this chapter teaches you how to effectively become a better apprentice, as well as how to use your experience of problem-solving to become a better member of a programming community later on. While the beginning of this Pattern explains the pouring of the tea cup, it is actually a metaphor that pertains to “clearing your mind of bad habits”, meaning that you should remove all distractions or de-motivators so that you can think more openly about the subject in mind.

I find it very interesting that the rest of the sections describing the “tools” to start your apprenticeship are sorted in a specific manner so that you yourself are familiar with all the experiences and different skills needed to learn a programming language. For instance, there is a problem and solution section in the “Concrete Skills” Section of “Emptying the Cup” that caught my attention. The problem state that a team believes that you cannot write a program, but that is where the solution tells you to learn about building concrete skills in order to convince the team to trust in you to be able to do the work. While the rest of the sections does not touch upon anything specific to programming, they all help you in showing your capability to help with coding.

What I found useful in this chapter was the “Your First Language” section that talked about the effective ways of becoming a better programmer. In the “Solution” section, there’s a specific segment where programmer Ralph Johnson explains in an interview on learning a programming language. When asked about which programming language to start with, he say “…the best way to learn a language is to work with an expert in it. You should pick a language based on people who you know. One expert is all it takes, but you need one.” Like the rest of the “Solution” section describes, I have learned that it is better to learn a programming language when you have an person or a group who can help guide you to writing better programs.

While the rest of the sections start to describe the many different problems and solutions to specific problems, the main takeaway I have from reading this apprenticeship pattern is to openly express your problems and work with your team on showing your commitment to programming in your career. I did not have any specific disagreements with this pattern, but I am still am curious about what the other sections have to show for learning the other patterns.

From the blog CS@Worcester – Elias&#039; Blog by Elias Boone and used with permission of the author. All other rights reserved by the author.

Security Testing

In software development security testing is very important to making sure applications are strong enough against cyber attacks. Security testing encompasses a variety of practices like, application security testing, and penetration testing.

Overview of Security testing

For this blog post, I chose the article ” Security Testing from Bright Security. The article provides a lot of insight on security testing, it’s goal, benefits of security testing, key principles, and the different types of security testing.

1.) Goals: The article showcases the main goals of security testing, which are realizing what assess needs protection, identifying the potential threats and vulnerabilities, evaluate the risks that come with the vulnerabilities.

2.) Key Principles: The article covers the main key principles of security testing, which are availability, integrity, authentication, and authorization. These principles make sure that important/sensitive information is accessed only by authorized users, and that it remains accurate and trustworthy.

3.) Different types of Security Testing:

. Penetration Testing: This security testing method replicates real world cyber attacks to test the effectiveness of already existing security measures.

. Application Security Testing: This security testing method finds and eliminates the vulnerabilities within software applications.

. Web Application Security Testing: This security testing methods test different techniques that gauges the vulnerability of web applications.

. Security Audits and risks Assessment: This is a test method that checks to make sure that everything is structured properly and in compliance with the rules/standards.

4.) Benefits of Security Testing:

. Early Detection of Vulnerabilities: Security testing allows for the early recognition of potential security issues, reducing the risk of exposure.

. Risk Management: When the vulnerabilities are identified, then we can create solutions to solve the risks of a cyber attack or data leak.

. Trust and Cost Efficient: Early detection of risks and vulnerabilities will not only enhance the rust of customers but it will significantly reduce the cost of a data breach and various fines.

Why I picked this Resource

I picked this resource because it provided a comprehensive and detailed overview of Security Testing. This Article had a lot of similarities with the topics that we covered in our course. Also, the article makes it easier to understand the nature of security testing and various practices and principles associated with it.

Personal Reflection

Reading this article expanded my understanding of security testing beyond what we learned in class. I learned how important it is to just about everything related to technology. Identifying threats, risks, and vulnerabilities and how each of these things come together to reduce cyber attacks. One thing that I can takeaway from this is learning about the various types of Security Testing and each one does something different, but all have a similar goal.

In my future endeavors, I plan on using what I have learned about these Security Testing principles by implementing them on future projects. This new found knowledge will help me to make better decisions in the future.

The full Article is here:
https://brightsec.com/blog/security-testing/

From the blog CS@Worcester – In&#039;s and Out&#039;s of Software Testing by Jaylon Brodie and used with permission of the author. All other rights reserved by the author.