Category Archives: CS-443

Mocking: Benefits & Drawbacks

Photo by Blue Bird on Pexels.com

When working on a piece of software or project, often I will encounter a point where some particular piece of the program cannot be tested properly due to one or more components or features not being completed yet. Rather than waiting until all these things are completed, and potentially rushing them to completion in order to do testing, mocking allows me to bypass the issue and use a “mock” version of the missing dependency or component needed for testing.

This is a convenient way to get around having to develop the entire program all at once before being able to test anything, which could arguably lead to more errors or issues over time since you then aren’t able to test as you implement features, but rather only after they have all been implemented. This aspect alone makes mocking seem like an overtly beneficial concept. But there are some drawbacks to consider with mocking, some of which I will discuss below.

When researching the concept of mocking, I found this article: (https://www.accenture.com/us-en/blogs/software-engineering-blog/to-mock-or-not-to-mock-is-that-even-a-question) to raise some good points in regards to both the upsides and downsides of the practice. The author of this blog post brings up three major downsides to mocking, those being that a.) mocking can violate the DRY principle (https://thevaluable.dev/dry-principle-cost-benefit-example/) ‘Don’t Repeat Yourself’, b.) mocking can complicate refactoring or reorganizing code, c.) mocking can add to the complication of your code in general.

In regards to the first issue, repetition, I think that in some cases that this can be justified, and while mocking does represent some duplication in that it is taking a preexisting piece of the program and creating a placeholder for it, this seems unlikely to have the same negative effects that say, having three different train classes which only differ in the value of their color attribute.

The second and third points regarding refactoring and complication of the program seem like legitimate pitfalls to watch out for. When restructuring, the various mocks present could no longer be applicable based on changes in the location of various dependencies and test-cases in relation to them. Mocking does introduce an additional layer of complexity which would not otherwise be present, definitely a fair concern to consider.

Overall, I would say that most of the issues associated with mocking which are described by the author can be mitigated or completely avoided provided that they are considered during development. Mocking still seems in general to be a benefit in relation to testing during development rather than after it has been feature-completed.

Articles Referenced:

https://www.accenture.com/us-en/blogs/software-engineering-blog/to-mock-or-not-to-mock-is-that-even-a-question

https://thevaluable.dev/dry-principle-cost-benefit-example/

From the blog CS@Worcester – CodeRoad by toomeymatt1515 and used with permission of the author. All other rights reserved by the author.

Software Testing: The Best Testing Tool

As stated in my previous post, I did a group honors project where each member covered a different software testing tool. The primary reason for this was to determine which tools were useful for what purpose, one of which, being Jacoco, I covered in the previous post as well. But, after having heard each group member’s analysis of their tools it begged the question, which tool is best? To properly discuss this I will be examining the pros and cons to each of the tools my group member’s covered in addition to a brief summary of Jacoco.


First there is PIT, which is a mutation testing tool. I was not familiar with these different types of tests, so I will give a quick explanation of what they are. Mutation testing involves the tool essentially making multiple copies of the code which are then given faults, these being the titular mutations. Tests are then run against each mutant with the result being the mutant’s death, meaning the tests failed, or the mutant surviving, meaning the tests passed. The quality of these tests is then determined by the amount of mutants that could be killed. This method of testing is very robust and covers code coverage as well, but this results in these tests being slower than others. PIT is advantageous as it is the fastest of the mutation tests, and has a lot of support from build tools. The other type of testing covered was Programming Mistake Detector, or PMD for short. This tool focuses on finding more expected flaws, like unused variables, useless objects, empty catch blocks, and other such errors we are all familiar with. In addition to this it can also spot any duplicated code with a copy-paste-detector. This is all handled by rules used to test source code, which are grouped into rule sets. Unlike mutation tests, this is much more lightweight and easy to run from a command line, additionally you can run multiple rulesets at once. However this is a less robust testing method, not accounting for any runtime issues or a way to match a ruleset with its generated error easily when running rulesets on a file. So what is the verdict?

Ultimately, there is only really one type of testing that seems sufficient to be used in and of itself, this being mutation testing with PIT. If you are looking for the most condensed testing tool that can do the most in one package, this would be the answer. However you will want to ensure that using mutation testing is necessary, as if you are only really concerned about code coverage Jacoco would be easier and more efficient. Simultaneously, if you want to check the code itself to ensure there are not many errors and do not care about the coverage, you can implement PMD. The answer will ultimately vary from system to system, and if you want to read more about these tools I have provided some resources below. If you want to read about Jacoco be sure to check my previous post!

From the blog CS@Worcester – My Bizarre Coding Adventures by Michael Mendes and used with permission of the author. All other rights reserved by the author.

Integration Testing

This week I wanted to look at integration testing. Most projects have multiple components coded by different programmers. The purpose of integration testing is to expose any defects in the way these components interact with each other. It is sometimes called ‘I & T’ (Integration and Testing), ‘String Testing’, or ‘Thread Testing’.

Integration testing is the next level of testing after unit testing. While each individual component of the software is unit tested, there may still be defects. These may be because:

  • A module written by one programmer may have different logic and understanding of requirements from another causing their two components to work together improperly.
  • New requirements may be added by the customer that need to be tested.
  • The software may not be interacting with the database correctly.

There are multiple approaches to integration testing. Big bang testing is integrating all the components at once and testing as a unit. This works well for small systems, but it may be hard to isolate issues. Incremental testing starts with only a few modules ad gradually adds more after testing for proper function. This can be done bottom-up by testing lower-level modules first, or it can be done top-down with higher level modules first. There is also the sandwich method which tests higher level and lower level simultaneously and then combines the two.

While we have not yet taken a look at integration testing in class, I wanted to have a rough idea of what it is at least. I am sure our POGIL activities will go more in depth and explore why integration testing is used as well as exactly how it is done.

https://www.guru99.com/integration-testing.html

From the blog CS@Worcester – Half-Cooked Coding by alexmle1999 and used with permission of the author. All other rights reserved by the author.

DD path graphs

Structural testing is based on the source code of the program under test, rather than the definition. This is known as white box testing, while functional testing is known as black-box testing.

Program diagram: For a program written in an imperative programming language, the program diagram is a directed graph with nodes representing statement fragments and edges representing control flow.

DD – path

DD path: Decision-to-decision path (Miller). Begin with the “exit” of the decision statement and end with the “path” of the next decision statement.

DD chain: A path of starting and ending nodes at different points in a directed graph.

Consisting of a node, the internality =0;

Consisting of a node, externality =0;

Consisting of a node, the inner degree > =2 or the outer degree > =2;

Consisting of a node, the degree inside =1 and the degree outside =1;

Length > =1 for maximum drill

DD – path graph

A DD-path graph is a labeled directed graph in which nodes represent the DD Path of a program graph and edges represent the control flow of the Path.

For a given program, many different program diagrams can be constructed, all of which can be reduced to a unique DD-path diagram.

It is possible to generate DD paths for programs up to 100 lines, and above that size, analysis tools are generally required.

The simplest control flow diagram is the DD path. DD can be thought of as belonging to the control flow diagram.

DD path definition:

Given a program written in an imperative language, its DD path graph is a directed graph, where nodes represent the DD paths of its program graph and edges represent the control flow between successive DD paths.

In fact, the DD path graph is a compressed graph in which 2-connected components are compressed into a single node corresponding to the 5DD path.

source:

Click to access 08-PathTestingCoverage.pdf

From the blog haorusong by and used with permission of the author. All other rights reserved by the author.

Software Quality Assurance and Testing Blog Post #4 (Program Graphs and DD Path Graphs)

The latest topic in my Software Quality Assurance and Testing class has been learning how to read and write program graphs and DD path graphs. We also have been learning how to make DD path graph tables that correspond with the DD path graphs and the program graphs. Our most recent assignment was all about these topics. With the amount of work we have put into learning these, the assignment proved to not be too difficult for me. It is generally easy to know how to read the program graphs because pretty much every node corresponds with a single line of code in a program. Where it gets tricky is when the program has loops and if statements. Converting to a DD path graph from the program graph is also not too hard if you know what you are doing. A lot of nodes from the program graph can be clumped together into single nodes (like all the instance variables for example). Lastly, creating the DD path graph table takes in account both the program graph and the DD path graph, but again is not too difficult to read or fill out. The amount of time we have spend on these topics in class while doing in-class activities has helped me tremendously, because looking at the graphs without knowing what you are looking at can be daunting and confusing. As much as I enjoy being able to understand the graphs and make them if I want, it is even more enjoyable knowing that these topics can all be used in real life. It is always very reassuring when I try to research topics from class and they seem to be very prominently used in the real world with lots of examples and helpful sources to teach me about them. In this case, these topics all seem to be used a lot and also seem to be very helpful for those who have used them before to study how a program could be working (or how it could be failing).

From the blog CS@Worcester – Tim Drevitch CS Blog by timdrevitch and used with permission of the author. All other rights reserved by the author.

Erockwood Post #1

In my operating systems class, we are learning about all of the important roles the operating system has in a computer. There are many managers that the operating system itself manages to keep everything running as smoothly as possible. One thing I am most interested in when thinking about operating systems is concurrency and multithreading. Most tasks are done serially, which is to say one at a time. This worked well back in the day when most computers had only one or two cores. Nowadays, however, CPUs usually have 4+ cores, and if all tasks an operating system does are serial, then 3/4 of the CPUs resources are not being utilized. This is where concurrency / parallelism / multithreading comes into play. With them, we allow programs/tasks to run simultaneously, using more than one CPU core, to do more work in less time.

The only problem with this is trying to write the programs/tasks to take advantage of more cores. I know when I first tried to create and run a simple C program that would use more than one core, it was very confusing on trying to have everything be worked on by a separate thread. Eventually, after some trial and error, I got it working, and it slowly started to make sense on how to write this type of program. In doing some very simple calculations like I was trying to do in my example program, there is not much performance to be gained from multithreading the program, but in much bigger programs with bigger calculations to be done, there is definitely some time that can be saved by multithreading the process.

From the blog CS@Worcester – Erockwood Blog by erockwood and used with permission of the author. All other rights reserved by the author.

Learn How You Fail

Failing is unavoidable and it happens to everyone at some point. In reality, anyone who has never failed at anything has either stopped challenging themselves or learnt to ignore their own mistakes.

Your skills are progressing but you are still failing and have some remaining weaknesses. So instead of drowning in a sea of self-pity over previous failures, develop some self-awareness of your own patterns, habits, behaviors, and other factors that play a role in causing you to fail. When you are aware of yourself and the things that mess you up, you give yourself the option of either trying to solve the issues or cut your losses. You cannot succeed in all, and recognizing your shortcomings is critical because it allows you to actively recognize distractions and concentrate on your goals.

Accept that there will be certain stuff you aren’t very good at.

Once you learn to accept your failures you can set realistic limitations on your goals.

Often times I get irritated by the fact I keep failing, but if I accept it, my failures become learning opportunities. I can then seek out guidance and learn new skills from someone who knows more about code than I do. It is hard, honest work to admit your shortcomings and it will take a lot of failures, but I am confident that eventually, it will all be worth the frustration.

From the blog cs@worcester – Coding_Kitchen by jsimolaris and used with permission of the author. All other rights reserved by the author.

Software Testing: Jacoco

For this week’s blog post I chose to focus on a topic that I had to research for an honors project, this being Jacoco. In case you are not familiar with it, Jacoco is a software tool that is used to test code coverage, which in short just involves checking how much of a system’s code is being tested. This may sound redundant if you, like myself, have only really been using tests on small scale projects, but as a program’s complexity increases so does the complexity of the tests required. Additionally you will have to write more and more tests, making it difficult to determine exactly what has been covered. Before anything else however, I am going to break down exactly how code coverage is checked.


Having not known about Jacoco at all, I found this article on Baeldung that was extremely useful in understanding how it worked. As I stated the essential idea is to check code coverage, which consists of line and branch coverage. Line coverage essentially means Jacoco will check how many lines of code are being executed during tests to determine which are actually being tested. Branch coverages is similar to this, but focuses on checking the logical splits in the program, such as conditionals like if statements. The total amount of branches present is then compared to the amount that were covered to give the results. There is one more aspect of a program Jacoco checks for, being cyclomatic complexity. This is essentially a measure of the logical complexity of the program, or how many branches the program has. Now that we know what Jacoco is we can discuss why you might want to use it.

This testing tool certainly has some major benefits for anyone working on a large system. If you are working on writing tests for a system, it may begin as something straightforward, but as the system grows it will become increasingly hard to track exactly what components are being tested. This is where Jacoco comes in, allowing you to see exactly what lines, or just classes, are being tested so you can ensure there is sufficient test coverage. Additionally it can show you the degree of logical complexity present in the system, which could then be adjusted accordingly. One point to keep in mind that is mention in this article is that you should not get tunnel vision when trying to increase code coverage. High code coverage does not necessarily mean the tests being run are adequately testing the system. It is more important to focus on writing a few functional tests for each component rather than just having a plethora of tests that do not really do much of value. This article discusses Jacoco in more detail than I can so if this interests you I would recommend checking it out!

Source:

https://www.baeldung.com/jacoco

From the blog CS@Worcester – My Bizarre Coding Adventures by Michael Mendes and used with permission of the author. All other rights reserved by the author.

Code Review

Link: https://smartbear.com/learn/code-review/what-is-code-review/

According to the article linked above, to code review is to consciously and systematically convene with one’s fellow programmers to check each other’s code for mistakes. There are many different ways to code review such as through an email thread, pair programming, over the shoulder, or tool assistance. Through email, a programmer can easily send their colleague a file with the un-reviewed code allowing both of them to exchange regardless of schedule or location. Pair programming is when two software developers work on the same code side by side, allowing both to more fluidly exchange ideas. Developing over the shoulder is similar to peer programming except only one developer actively works on the code while the other acts as support. Tool assistance allows for even more flexibility than email chains since it allows for code review through software-based code review tools.

Code review is a very simple concept as it is simply peer reviewing in the context of software development. The importance of peer review is that another person can examine and point out someone’s mistakes unlike automated processes. Developers are still human and make human errors which are less likely to be picked up through artificial means. And solely relying on tests written by the original developer runs the risk of overlooking errors not picked up by tests or in the worst case, following the assessments of a flawed test. That’s why peer review, and by extension code review, is so important to speeding up and streamlining software development.

From the blog CS@Worcester – Rainiery's Blog by rainiery and used with permission of the author. All other rights reserved by the author.

The difference between Stubs and Mocks

A mock is an object that can set an expected value, which will verify that the desired action has occurred. The stub is the object that you pass to the code under test. You can set expectations on it to work in specific ways, but those expectations are never verified. The stub’s properties will automatically behave as standard properties, and you cannot set expectations on them.

If you want to verify the code’s behavior under test, a simulation with appropriate expectations is used and validated. If you’re going to pass only values that might need to work somehow but are not the focus of the test, you can use stubs.

Important note: Stubs will never cause a test to fail. 

I believe the most significant difference is that you have written the stub with predetermined behavior. Thus, you will have a class that implements the dependencies that you have disguised for testing purposes (most likely an abstract class or interface), and a class will handle the method only through the response that is set. They don’t do anything fancy, and you’ve already written stub code for them outside of testing.

Simulations are expectations that stubs must set during testing. The simulation is not set up in a predetermined way, so you have the code to perform the simulation in your tests. The mockery is determined at run time because the code that sets expectations must be run before they do anything.

The difference between stubs and stubs

Tests written with simulation usually follow a test pattern of initialize-> set expectations -> exercise -> verify. The pre-written stub will be followed by an initialize-> exercise-> verify.

Similarities between stubs and stubs

The purpose of both is to remove all dependencies from testing a class or function so that your tests are more focused and straightforward when trying to prove it.

The most crucial difference between Stubs and Mocks is their intention. Explain this in the WHY stub and WHY simulation below

Suppose I’m writing test code for the Mac Twitter client’s public timeline controller

Here is the sample code for the test:

twitter_api.stub(:public_timeline).and_return(public_timeline_array)

client_ui.should_receive(:insert_timeline_above).with(public_timeline_array)

controller.refresh_public_timeline

Stub: The network connection to the Twitter API is very slow, which makes my tests slow. We knew it would return the timeline, so we made a stub that simulated the HTTP Twitter API so that our test could sprint, even if I were offline.


Mock: We haven’t written any UI methods yet, and I’m not sure what we need to register for UI objects. We wanted to write test code to see how my controller would work with UI objects.

In short, there is a difference between Mock and Stub objects, and RhinoMocks recognize that they allow us to write tests that better illustrate their purpose. The mock object is used to define the expectation that, in this case, I want to call the method A () with such an argument. Record and verify this expectation with ridicule. On the other hand, Stubs have a different purpose: they do not record or validate expectations but rather allow us to “replace” the behavior and state of “fake” objects to take advantage of test scenarios.

https://martinfowler.com/articles/mocksArentStubs.html

View at Medium.com

From the blog haorusong by and used with permission of the author. All other rights reserved by the author.