Just a second! 🫷 If you are here, it means that you are a software developer.
So, you know that storage, networking, and domain management have a cost .
If you want to support this blog, please ensure that you have disabled the adblocker for this site. I configured Google AdSense to show as few ADS as possible – I don’t want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.
Thank you for your understanding. – Davide
You surely take care of your code to make it easy to read and understand, right? RIGHT??
Well done! 👏
But most of the developers tend to write good production code (the one actually executed by your system), but very poor test code.
Production code is meant to be run, while tests are also meant to document your code; therefore there must not be doubts about the meaning and the reason behind a test.
This also means that all the names must be explicit enough to help readers understand how and why a test should pass.
This is a valid C# test:
[Test]publicvoid TestHtmlParser()
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml("<p>Hello</p>");
var node = doc.DocumentNode.ChildNodes[0];
var parser = new HtmlParser();
Assert.AreEqual("Hello", parser.ParseContent(node));
}
What is the meaning of this test? We should be able to understand it just by reading the method name.
Also, notice that here we are creating the HtmlNode object; imagine if this node creation is present in every test method: you will see the same lines of code over and over again.
you can understand its meaning by reading the test name
the code is concise, and some creation parts are refactored out
we’ve well separated the 3 parts of the tests: Arrange, Act, Assert (we’ve already talked about it here)
Wrapping up
Tests are still part of your project, even though they are not used directly by your customers.
Never skip tests, and never write them in a rush. After all, when you encounter a bug, the first thing you should do is write a test to reproduce the bug, and then validate the fix using that same test.
LINQPad is one of the tools I use daily. But still, I haven’t used it at its full power. And you?
Table of Contents
Just a second! 🫷 If you are here, it means that you are a software developer.
So, you know that storage, networking, and domain management have a cost .
If you want to support this blog, please ensure that you have disabled the adblocker for this site. I configured Google AdSense to show as few ADS as possible – I don’t want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.
Thank you for your understanding. – Davide
LINQPad is one of my best friends: I use it daily, and it helps me A LOT when I need to run some throwaway code.
There are many other tools out there, but I think that LINQPad (well, the full version!) is one of the best tools on the market.
But still, many C# developers only use just a few of its functionalities! In this article, I will show you my top 5 functionalities you should know.
Advanced Dump()
As many of you already know, to print stuff on the console you don’t have to call Console.WriteLine(something), but you can use something.Dump();
void Main()
{
var user = new User(1, "Davide", "DavideB");
user.Dump();
}
You can simplify it by avoiding calling the Dump operation in a separate step: Dump can print the content and return it at the same time:
var user = new User(1, "Davide", "DavideB").Dump();
For sure, this simple trick makes your code easier to read!
Ok, what if you have too many Dump calls and you don’t know which operation prints which log? Lucky for us, the Dump method accepts a string as a Title: that text will be displayed in the output panel.
var user = new User(1, "Davide", "DavideB").Dump("My User content");
You can now see the “My User content” header right above the log of the user:
Dump containers
We can do a step further and introduce Dump containers.
Dump Containers are some sort of sink for your logs (we’ve already talked about sinks, do you remember?). Once you’ve instantiated a DumpContainer object, you can perform some operations such as AppendContent to append some content at the end of the logs, ClearContent to clear the content (obviously!), and Dump to display the content of the Container in the Results panel.
DumpContainer dc = new DumpContainer();
dc.Content = "Hey!";
dc.AppendContent("There");
dc.Dump();
Note: you don’t need to place the Dump() instruction at the end of the script: you can put it at the beginning and you’ll see the content as soon as it gets added. Otherwise, you will build the internal list of content and display it only at the end.
So, this is perfectly valid:
DumpContainer dc = new DumpContainer();
dc.Dump();
dc.Content = "Hey!";
dc.AppendContent("There");
You can even explicitly set the content of the Container: setting it will replace everything else.
Here you can see what happens when we override the content:
Why should we even care? 🤔
My dear friend, it’s easy! Because we can create more Containers to log different things!
Take this example: we want to loop over a list of items and use one Container to display the item itself, and another Container to list what happens when we perform some operations on each item. Yeeees, I know, it’s hard to understand in this way: let me show you an example!
DumpContainer dc1 = new DumpContainer();
DumpContainer dc2 = new DumpContainer();
dc1.Dump();
dc2.Dump();
var users = new List<User> {
new User(1, "Davide", "DavideB"),
new User(2, "Dav", "Davi Alt"),
new User(3, "Bellone", "Bellone 3"),
};
foreach (var element in users)
{
dc1.AppendContent(element);
dc2.AppendContent(element.name.ToUpper());
}
Here we’re using two different containers, each of them lives its own life.
In this example I used AppendContent, but of course, you can replace the full content of a Container to analyze one item at a time.
I can hear you: there’s another question in your mind:
How can we differentiate those containers?
You can use the Style property of the DumpContainer class to style the output, using CSS-like properties:
DumpContainer dc2 = new DumpContainer();
dc2.Style = "color:red; font-weight: bold";
Now all the content stored in the dc2 container will be printed in red:
Great stuff 🤩
Read text from input
Incredibly useful, but often overlooked, is the ability to provide inputs to our scripts.
To do that, you can rely on the Util.ReadLine method already included in LINQPad:
string myContent = Util.ReadLine();
When running the application, you will see a black box at the bottom of the window that allows you to write (or paste) some text. That text will then be assigned to the myContent variable.
There’s a nice overload that allows you to specify a sort of title to the text box, to let you know which is the current step:
Paste as escaped string
This is one of my favorite functionalities: many times I have to escape text that contains quotes, copied from somewhere else to assign it to a string variable; I used to lose time escaping those values manually (well, using other tools that still are slower than this one).
Assigning it manually to a string becomes a mess. Lucky for us, we can copy it, get back on LINQPad, right-click, choose “Paste as escaped string” (or, if you prefer, use Alt+Shift+V) and have it already escaped and ready to be used:
We’ve seen 5 amazing tricks to get the best out of LINQPad. In my opinion, every C# developer that uses this tool should know those tricks, they can really boost your productivity.
Did you already know all of them? Which are your favorites? Drop a message in the comments section or on Twitter 📧
Just a second! 🫷 If you are here, it means that you are a software developer.
So, you know that storage, networking, and domain management have a cost .
If you want to support this blog, please ensure that you have disabled the adblocker for this site. I configured Google AdSense to show as few ADS as possible – I don’t want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.
Thank you for your understanding. – Davide
Mixed levels of abstraction make the code harder to understand.
At the first sight, the reader should be able to understand what the code does without worrying about the details of the operations.
Take this code snippet as an example:
publicvoid PrintPriceWithDiscountForProduct(string productId)
{
var product = sqlRepository.FindProduct(productId);
var withDiscount = product.Price * 0.9;
Console.WriteLine("The final price is " + withDiscount);
}
We are mixing multiple levels of operations. In the same method, we are
integrating with an external service
performing algebraic operations
concatenating strings
printing using .NET Console class
Some operations have a high level of abstraction (call an external service, I don’t care how) while others are very low-level (calculate the price discount using the formula ProductPrice*0.9).
Here the readers lose focus on the overall meaning of the method because they’re distracted by the actual implementation.
When I’m talking about abstraction, I mean how high-level an operation is: the more we stay away from bit-to-bit and mathematical operations, the more our code is abstract.
Cleaner code should let the reader understand what’s going on without the need of understanding the details: if they’re interested in the details, they can just read the internals of the methods.
We can improve the previous method by splitting it into smaller methods:
Here you can see the different levels of abstraction: the operations within PrintPriceWithDiscountForProduct have a coherent level of abstraction: they just tell you what the steps performed in this method are; all the methods describe an operation at a high level, without expressing the internal operations.
Yes, now the code is much longer. But we have gained some interesting advantages:
readers can focus on the “what” before getting to the “how”;
we have more reusable code (we can reuse GetProduct, CalculateDiscountedPrice, and PrintPrice in other methods);
if an exception is thrown, we can easily understand where it happened, because we have more information on the stack trace.