Poor Man’s PiTest

Unit testing, as important as it is, is still treated as an afterthought. First, we write production code and then tests — if we have enough time and, more importantly, the will. Unfortunately, the natural result of this process is missing coverage, weak or absent assertions, and, more importantly, a lack of real exercise of the production code. I believe these issues are well known, but somehow they’re still not important enough to change the process. After all, we have production code; let’s ship it — “Damn the torpedoes, full speed ahead!”


In my opinion, this issue is well addressed by Kent Beck and Robert C. Martin (Uncle Bob), who emphasize starting with a test. Unfortunately there seems to be lots of fluff, buzz and general misunderstanding of TDD. No, it is not Test-First. No, there is no magic sauce. No, there is no need to read tons of books or go to fancy lectures. After reading a few books, watching a few videos, attending a few fancy lectures and a few years of relentless practice, I can definitely say that all you essentially need is to read Kent Beck’s book – Test-Driven Development: By Example, watch Robert C. Martin and stick to The Three Rules of TDD. While Kent and Robert do not address every single scenario and issue, the above resources are more than enough to extrapolate and adapt the technique, let’s say to microservice architecture (which is not originally addressed). Now I’m not saying those are all the resources that you will ever need, but those are essential for TDD! Last but not least, if you don’t know how to do dependency injection without @Autowire (unfortunately I’ve seen that), it does not mean TDD sucks, it means you lack knowledge. TDD is not magic or a Swiss Army knife, it is a specialized technique for writing code independent of any specific platform, framework or language.

Example of useless 100% code coverage

Unfortunately TDD is not widely adopted, and even if it was, we are still subject to mistakes — we are only human. Under these conditions we need a solution that does not rely on a human. One bad solution is code coverage. Code coverage has been around forever and is loved by managers as it neatly translates to charts, means little, and can easily be faked by developers. Code coverage has its uses, but it is useless in determining how well production code is actually tested. But don’t despair, we do have a solution — mutation testing. Mutation testing has a simple premise: change production code, see if any tests fail. If nothing is failing after the change, that means insufficient testing. In an inverted way it sure resembles TDD. While the premise is simple, execution is quite expensive! See how long your test set takes, then see how many logical and state changes can be applied in production code, multiply those numbers and you see how much time mutation testing will take — in short, a while for a sufficiently large project. That is probably the reason we don’t see mutation testing everywhere.

Mutation testing is hard to fool

Fortunately the Java world is blessed with a modern tool for mutation testing – PiTest. It provides lots of features in its open source form. Even more features come with the commercial extensions, which integrate into a modern pipeline (or dev machine) and provide quick feedback to developers waiting for code review on their code changes. I wholeheartedly suggest buying a license and using the full power of mutation testing if code quality is important. Unfortunately my company, for various reasons, isn’t quick to sign up, despite claims to strive for quality. After a proof of concept, presentations, near begging and a final rejection, I fell into depression. I really wanted to use PiTest for tangible results and partly because it would have made my life a lot easier and the life of junior developers a lot harder – since I would not have to spend any time figuring out if their test code actually tested enough or anything at all (yeap, I’ve seen those cases too).

Finally, I realized I’m an engineer; my realm of possibilities lies far beyond microservices. I decided to build a “Poor Man’s PiTest”: a small tool that takes the open-source core and combines it with the power of Bash, Git, and a few other Linux commands. That gave me the ability to build only the projects that are needed, run PiTest only on the changed classes in a multi-module Maven project with a mix of JUnit 4 and JUnit 5 modules, perform timing analysis, log everything for debugging purposes, and optionally skip builds and/or focus on a single module if needed. So far it works well and, let me tell you, junior developers just “love it” — especially when I give them a long report of everything that’s been missed.

I figured someone might be in a similar situation, perhaps needs a bit of inspiration or an example, so I created a GitHub repository to share the script.

Thank you, and I hope the script will be of some use.

Cost Reasoning

Cost seems to be an inseparable part of our lives. We look at it when we buy stuff, pay taxes, purchase services. But it takes a bit of a backseat when it comes to computer science—especially during the academic years. It comes back once we join the workforce, though it often takes on an elusive form — at least in my IT experience. It doesn’t come naturally; it’s effectively forced on us through communication with the business side. To me, “cost” is so ill-defined that it’s often thrown around for emphasis rather than as something more or less concrete.

Even in personal life, cost is tricky. Let’s say a pen costs $1. Is that the real cost? Do we just buy it and move on? Well, here’s how I see it: the pen costs $1 + $0.13 (13% sales tax) = $1.13. Now factor in income tax—say, 30%—and the cost becomes $1.13 / 0.7 = $1.61 (rounded). So once we factor in both sales and income taxes, the cost jumps by about 61.4% from the sticker price. And that doesn’t even include the cost of earning that money in the first place. Do you drive to work? How long does it take? Do you pay for insurance? Is your car depreciating? If you run a business, you might write those expenses off—but if you’re an employee, you just eat the cost. By the time all’s said and done, you’re looking at a markup of over 61.4%—and that’s before environmental fees, dealer fees, or whatever else sneaks in at checkout. And finally: how much time does it actually take to make that money?

Now to the IT world. I remember a while back when agile cards (or T-shirts and such) were a popular tool to estimate work for a sprint. At some point, that idea took a dive—never to be seen again. Why? I believe it comes down to two things: inaccuracy and waste. You end up estimating in points that are hit or miss (most of the time, a miss). Then managers try to make sense of those points for that particular team and convert points to time. Eventually, they give up and just ask for time estimates instead. Next, all of that gets applied to project estimates, and finally, to the budget. Eventually, the whole exercise was written off as useless. Why waste time estimating when it doesn’t improve accuracy? It just ends up wasting time for no benefit—or in other words, increasing cost.

Next up: software craft. How do we reason about the cost of code? Do we think about execution cost? Development cost? What about refactoring, adding features, maintenance, or security upgrades and dependencies? Do we factor in code correctness? Recently I was part of an architectural decision-making process. We had two paths: develop a new feature using an old dependency or a new one. Let’s break them down.

Using a new dependency: faster, easier development, with future support. Risk: the new feature might not integrate easily with the legacy system.

Using the old dependency: easier integration. Risk: slower development, no support, potential security issues. And when the old system is retired, we’ll either have to rewrite everything or keep dragging around old dependency — bringing all the baggage with it.

Moreover, we know the old system is set to be retired within the next six months, while new feature delivery is targeted for the next twelve. Just from the timeline alone, it becomes painfully obvious that the risks of supporting the old system are already mitigated. From a development cost perspective, it makes far more sense to adopt the new dependency. Yet the issue keeps getting debated—because, well, “nobody gets fired for buying IBM.” The essential argument is that writing new feature code based on the old dependency will work “everywhere,” so the deadline will be met, and we’ll all be safe—no risk. But what about cost? It would take three times as long using the old dependency. What about future cost—what if we have to rewrite it later? How about support—dragging around an old, unsupported dependency isn’t free. And do we ever factor in security risks? How much will that cost? Yeah, at the beginning of the day, if we don’t consider cost—or worse, don’t communicate it to the business—we might feel “safe.” But the business can count. And usually, better than IT. So by the end of the day, cost questions will creep in—and by that point, no one will be safe.

The cost of software development is anything but trivial—it depends on a variety of factors. Maybe it’s a throwaway project. In that case, we can skip tests, write a mess of code, use bubble sort, and slap it all together just to get it running as quickly as possible. But should we do the same for a legacy project? What about a current production system? Do we write clean code so it pays off with the next feature set—or just duct-tape things together and leave it for someone else to debug at 3:00 a.m. on a Sunday during a production emergency? I believe a developer can make any choice—as long as it’s a conscious one, based on cost considerations. And to make that kind of decision, cost must be learned, understood, and applied—as part of both software education and everyday development.

Teaching programming to a kid

I have been slowly teaching my kid a bit of programming. Programming is not easy, and teaching it to a child is quite a challenge, so anything that makes it easier is welcome.

Initially, I have been using Scratch to teach programming, however, I moved away from it because it is not really that easy to use once you want to make something a bit more complex (even I had some issues following online tutorials) or teach a kid about some programming concepts such as for-loops.

Next, I tried Swift Playground, it is awesome, however, I got stuck on explaining for-loops. It might be easy for grown-ups to get a grasp of syntax and associated concepts, but for a child, it is a challenge.

I have been thinking about what to do next. Python? Well, maybe it is a good direction, but again syntax will get in the way of learning programming concepts… Today, I discovered Hedy and it looks very promising.

Checkout GOTO2022 talk:

5 topics for yearly knowledge refresh

Recently one more senior developer decided to leave my team and the company. The event got me a bit sad, not only the team is loosing a good developer but it also means new developer will be joining the team and that means teaching the developer all the ropes.

I have been through this few times now and its really starting to get to me. It takes time for a developer to learn how to write clean code, test drive, refactor, not to mention learn all the ins and outs of the company’s systems.

In addition I keep noticing that a developer can take expensive courses, lets say on TDD and still lag behind – missing tests or writing too many. That got me thinking, is it possible to improve the situation by creating yearly refresh courses? Effectively new developers get to know all the essentials of development at the company and present developers get to fresh up on the existing practices and perhaps come up with improvements (or trash something that no longer brings value).

So here are 5 topics:

  1. Clean code
    It is important to learn and practice writing clean, easy to read and follow code. Clean code is a foundational knowledge, it effect all other practices in very fundamental way (from production to test code).
  2. Unit testing and TDD
    Testing is the prerequisite for continues delivery. Every developer must understand the value of testing and how it enables continues delivery. TDD is the best developer technique for writing valuable tests with the maximum reasonable coverage. Tests is code, it requires maintenance, tests must bring value and TDD is well established technique for doing so.
  3. Refactoring
    No one ever designs or writes perfect code. Moreover no one has crystal ball that predicts future business needs. Refactoring is important skill for continuously changing, adopting and improving code and system design.
  4. Higher order testing
    Beyond a system boundary, there are more systems. Developer must understand techniques and tools that are available for Integration, Contract and E2E Testing. Pros and cons must be weighted carefully in order to provide meaningful automated testing and short lead times. 
  5. Pipeline and environment
    Software systems no longer built locally and run on bare metal. Pipeline builds systems and those systems run in virtual environments. While developers are not DevOps (and probably will never be), it is important for developers to know how pipelines are developed, employed and maintained. How systems are packaged and run in docker under Kubernetes.

Premature optimization, value and waste

Ah, premature optimization, every developer sooner or later hits that. You optimize code, iron it out so there are no extra cycles, no extra memory or what not and not terribly long after you have to take that carefully tailored code apart, just because a new requirement came in. Worse yet, the realization that the optimization was useless in the grand scheme of the app, yes the app works more efficiently but it does not affect anything at all. Some might be proud of the craftsmanship, others might be disappointed with the waste, in any case I’m not here to judge.

I am here to share a story about premature optimization but at a much higher level – a feature level. A few years back when I started writing my app I wanted it to have a feature, let’s call it “frequency determinator”- feed data to the app and it can determine how often an event occurs. In my mind, it was very cool to feed the app data and automagically determine a pattern. Well, I started with the determinator code, it was simple but working. I said “great, it is time to apply it to real data that I have”. But there was a problem, I didn’t have an app, it was just code for frequency generation, I would like to be able to upload data from my phone and see the magic. Ok, no problem, I thought to myself, I just need an UI. I wrote first version of UI, which looked very basic and was incredibly confusing to use at times even to myself. So I rewrote it, added few features, just so the app can be a bit more useful to me. I thought to myself: “well, now that I have UI would it nice to have this and that, I’ll hook up frequency determinator in next little while”. I showed my app to a friend but UI was still confusing. Ok, no problem, I rewrote it again and added more features. Since I already had some data in the app, it was becoming more and more useful. Along the way I did some more refactoring, added just few more features, changed the UI a couple more times and the app was shaping up more and more. I was happy, the app alleviate big chunk of my anxiety and with each new feature, it was becoming more valuable and refined.

Is it there yet? Nope, I still need to rewrite the UI, since there is lot of room for improvement in usability. I can definitely use couple more features and some additional features that I believe can reduce some of my anxiety. So when is “frequency determinator” coming in? I don’t know! As I was using the app, refactoring and adding features, I gradually realized that time for “frequency determinator” hasn’t come yet. There is no need and from the feedback I got, it might not be ever needed. Something that looked like great feature, a centre piece of the jewel, turns out otherwise. Once you use the app, you realize where the value is for you. Once others use the app, you see move value around. The value doesn’t exist in vacuum of your own ideas, value only exists when someone points to it.

So, what now, stop wasting time and start looking for value? I wish I can say that but in reality my ideas of “frequency determinator” kicked started initial development. I might not have started writing the app without believe that “frequency determinator” will be the key to solving my problems. So is there a value in a waste? I believe there is, we “waste time” but because of it, we come up with ideas. I think wasting time looking at the sky or laying around on the coach is actually a good thing. But nothing will come out of waste alone, entertain ideas enough, build something, use it and see if the value is there for you.

Springboot test custom client, controllers and/or filters – a quick way

Recently I posted a question to the stackoverflow (please check it out first). Unfortunately I didn’t have time to explain – ‘why?’

Also I don’t think stackoverflow allows lengthy debate in the comment section. So I would like to make a quick explanation and hopefully have a debate in the comments.

Ok, so, why would you want to test service client and associated controller (springboot service) in the same unit test? I believe the case is fairly narrow and following conditions should apply:

  • Microservice must come with an associated client, which is capable of executing all available endpoints (in my case: internal microservice policy)
  • Client is complex!
    • Serialization and deserialization of objects
    • Uniform handling of errors (internal errors)
    • Custom security (internal use)
    • Custom compression
    • Logging
  • Controller is thin (just a delegation to a business layer)
  • Limited/Inflexible build pipeline OR time constraint on unit test execution (let’s say if test takes more than 4-5 seconds)

Now here is a list of ‘usual suspects’ to why NOT test client and controller together:

  • Service and client are separate ‘units’ therefore SRP and/or separation of concerns
  • Client is simple and can be tested separately
  • Controller can be tested separately
  • Fast/flexible build pipeline and/OR no time constraint

I would like to defend my approach:

  • Unit test is very flexible term – I believe developer/business can define what unit actually means. In my case, internal policy states that if I develop new endpoint and/or service I must provide a client, that will comply with company’s internal needs. So a unit of work in this case is client and associated endpoint/s, one can’t exist without the other – therefore one unit.
  • Client is not simple at all. Luckily most of the internal logic is abstracted away, so I can reuse abstraction and focus on immediate things like: path params, path variables, method and payload – which should be tested.
  • Controller in my case is thin one – meaning there isn’t much code there, typically one line – delegation to business layer. So I can test controller separately, but there isn’t much value. The value of the thin controller is in correct delegation and entry point (paths & params specified correctly).
  • Build pipeline is important tool, however if it is slow, constrained and inflexible, it becomes major source of headache (and sometime creativity). If your test brings up service and in process takes up a port and 10-18 seconds to start up, well the test will be ignored/removed in the name of performance – no value in that.

I hope this reasoning (along with stackoverflow solution) will be useful and helpful for those in need. Please share your thoughts.

Thank you!

Angular RxJS unsubscribe as a feature design

After few hours of thinking and more hours of implementation, I finally proved my idea is workable. I found it be a bit intriguing and so let me share it. Before jumping to the matter at hand, I would like to briefly mention that:

  • I don’t believe in end-2-end testing
  • I prefer unit testing
  • I like to test-drive my code

A few more details can be found here. On top of the above, I dislike using mocks, spies, stubs and other fakes to make testing “easier”. Now don’t get me wrong I do use mocks a bit, but mostly at a boundary (calls to backend, db and such). Let’s be reasonable, if code is a mess and/or legacy or there is a time constraint, options tend to dwindle and if you have to mock, you mock.

Now let’s see about my situation: I have simple app with navigation bar, located at the very top of the screen and top of the component hierarchy. The navigation bar contains search component. Below I have the main space with a router that happily swaps out different main components, depending where you click.

There is no simple way to pass data from the search component to presently displaying main component, unless we use observable, in particular Subject observable. So we have search observable and a main component is subscribed to it. The question is: “how do we test unsubscribe in the main component when we navigate away and the main component is destroyed?”.

After some thinking, I realized that NOT every main component is searchable, in those cases search component can be hidden. The approach has elegance to it, since it will help with testing and at the same time enhance user experience by eliminating confusing search feature that doesn’t seem to do anything when non-searchable main component is displayed.

On the other hand, when main component is searchable, then search component must be displayed so user can search it.

Ok, so how do we go about killing two rabbits with one bullet? We will enhance the search observable so it will count how many subscribers it has. Whenever any main component subscribes or unsubscribes to the search observable, the count will go up or down. Next a bit of elbow grease to wire up search component to hide/show when the count is equals to or less than 1 and we are done.

The whole logic can be tested by checking if the search component is displayed or hidden via jasmine spec, by navigating between main searchable components and main non-searchable components. No mocks, no spies, just elegant, user friendly feature and code design.

sTool Request

Every developer is familiar with Pull Request, however recently I came across another useful practice – “sTool Request”. sTool request is a simple and powerful practice yet some, especially junior developers, hesitate to use it; at least I noticed the behaviour in my team and so it is a good time to talk about it.

Unless your team is practicing pair programming, everyone tends to work alone and often the only time you get to see work of another developer is at Pull Request time. While it is generally a good thing, there are few drawbacks:

  • Effort has been expanded – sunk cost bias has already kicked in
  • Work is completed – physically & physiologically – let’s move on to the next thing
  • Code has settled – mistakes are more expensive to fix
  • In case a bug is discovered at a later point, it is harder to figure from the original developer – rarely anyone remembers details of decision making and code several weeks back.

sTool Request can help remedy those issues to some degree, at the very least it is cheaper to brainstorm/discuss questions, issues and ideas prior to producing code, and/or at any point during the work. So here goes my proposal to a team:

“Got a question or a doubt? Perhaps you want to bounce ideas around? Ask a team member to grab a stool/chair, sit down and help you out. Use the momentum to have a rich conversation and/or debate. Don’t wait for PR (Pull Request), refactor card, or a bug report. We are all on the same team, working together to deliver team’s goal, improve quality and learn new things.”

I would love for people to pair program, but in the absence of the practice, it is useful to invoke “sTool Request”, summon a developer and work things out. There are so many times I though to myself: “if only a developer have asked prior, he wouldn’t have made those mistakes and also learned a ton from the experience”. It is harder to learn retrospectively, it is better to learn in the moment.