DaedTech

Stories about Software

By

Chess TDD 11: Tying Up Loose Ends

Now that all of the pieces are implemented, I decided to change it up a little and get rid of a few yellow items in one fell swoop. I thought it would also be a good time to
Here’s what I accomplish in this clip:

  • Move Knight into it’s own class.
  • Separated test and production into their own assemblies.
  • Got rid of the DefaultBoardSize constant that had been defined in many test classes by consolidating it into one place.
  • Went back to earlier test classes and implemented “MovesFromXX” pattern that I had settled on.

Here are some lessons to take away:

  • If Ctrl-M, O stops working (or any normal VS shortcut), it might be because some other process has claimed part or all of the sequence and is preempting Studio.  Kill processes or reboot.
  • If you have some mundane but necessary tasks (e.g. moving a bunch of classes into a new namespace), this can be a good way to “warm up” when you haven’t looked at a code base in a while.  Generally speaking, look for tasks that can ease you back into the code base while being productive.
  • Even duplication that is seemingly mundane should be avoided.  The DRY principle states that systems should have a single point of definition for all pieces of knowledge, and that includes things like database schemas, config files, and yes, even your unit test classes.  You need to maintain your unit tests, and duplication creates maintenance pain.
  • Pay attention to your test method names as you’re making changes.  These are documentation as to how your code should behave, so if you change the behavior, make sure to change the names to reflect those changes.
  • “Lean on the compiler” to help you with your refactorings.  If you’re getting rid of a bunch of references to a variable, delete the definition of the variable and the compiler will then force you to get rid of all references.  If you do it in the opposite order, you might miss references and waste time.  As a more general rule, you should always favor failing early and doing things in such a way that mistakes will result in non-compiling.
  • When you do large refactorings, it might not hurt to run all of the unit tests explicitly in the test runner instead of relying on the dots in NCrunch (or any continuous testing tool that you’re using).

By

TDD Chess Game Part 6: Starting with More Pieces

I recorded this clip at a home, where I have a pretty old dual core processor that’s really starting to show its age when I have the video capture software running alongside VS2013 with all of my plugins. So, if you see the recording looking a little choppy in places, it’s because I edited out some places where I finished writing a test and was just drumming my fingers waiting for the test to go green or something. No need to show you completely dead time. Note to self — probably time for a new machine here soon.

Here’s what I accomplish in this clip:

  • Pretty up Rook.GetMovesFrom() a bit with Linq via refactoring.
  • Added Bishop class.
  • Implemented Bishop.GetMovesFrom()

Here are some lessons to take away:

  • You can write a test that’s green from the get-go if you want another data point prior to a refactoring.  Strict TDD says you need a failing test before you modify production code, but you can write green tests to your heart’s content.
  • Sometimes inheriting from a base class or implementing an interface is the quickest way to get from non-compiling because of an undefined method to having that method defined.
  • Just stubbing out a test method doesn’t constitute a failing test.  As long as everything is green, refactoring is fine.
  • I personally find that there are occasions when extracting a method or defining a common method is actually the simplest way to get something to pass.  So, even though that seems really like a refactoring, it can be your simplest path to get a red test green because it saves re-coding the same thing repetitively.
  • It’s perfectly fine to leave a working, but ugly, method and come back the next day/session to refactor.  Often times a much more elegant implementation will hit you in the shower or on the drive to your office, and you’ll make short of cleaning up the method when you have this fresh perspective.

And, the video:

By

TDD Chess Game Part 4: Getting Organized

Alright, welcome back to this series.

A couple of housekeeping things:

  1. I have bitten the bullet and used the Visual Studio White theme along with 14 point font to record, so hopefully the videos going forward should be easier to watch. It’s a little surreal to work with, but c’est la vie.
  2. The source code is now available on github for you to follow along. The coding is usually running ahead of my publication, so if you want to see the code from a given video, you may have to grab a slightly earlier version.

Here’s what I accomplish in this clip:

  • Started using a little todo list to keep track of what I’ve done and what I need to do.
  • Cleaned up code as reported by static analysis tools.
  • Pulled some production classes into their own namespaces and out of the test classes.
  • Defined an abstract Piece class.
  • Defined a second inheritor, “Rook,” for Piece.
  • Defined a bit of dumb functionality for Rook’s “GetMovesFrom” to get it started.
  • Implemented ability for a pawn to move two spaces on its first move.
  • Defined a piece concept of “HasMoved.” (albeit just for Pawn)

And here are the lessons to take away:

  • Keeping a list of smallish things you want to change can help you keep track of what needs to be done without distracting you too much (I picked this technique up from Kent Beck’s “Test Driven Development By Example.”)
  • If you’re using NCrunch, use the green dots being dark or bright as a quick way to tell if the code is compiling.
  • Gamify cosmetic issues. If “Optimize Namespaces” and things like that are important, make violations ugly and distracting in the IDE and you’ll get annoyed and fix them whereas you probably wouldn’t bother, otherwise.
  • It’s okay to write stupid tests if you do so knowing that you’ll fix them. Finding ways to always write a test to change production code is good for practicing the TDD discipline until it starts to become second nature.
  • It’s okay to write a test that causes a non-compile failure and then needing to do a good bit of work to get everything back to compiling/passing.
  • I’ve mentioned this previously, but it bears repeating: it’s okay to reuse a test (especially a stupid one) to get a failing test.
  • If you weren’t aware of C# yield keyword and deferred execution, it’d be a good thing to familiarize yourself with.
  • Force yourself not to copy and paste as much as possible, even when it seems dumb. Feeling the pain of re-typing things will make it painfully obvious when you’re duplicating code and could do something better.

And, here’s the clip:

By

NCrunch and Continuous Testing: The Must-Have Setup

Most of this post was taken from the transcript of my Pluralsight course on NCrunch. If you are interested in watching the course but are not a Pluralsight subscriber, feel free to email me or leave a comment requesting a trial, and I’ll get you a 7 day subscription to check it out.

Understanding the Legitimate, Root-Cause Objection to TDD

In my experience, there are three basic “camps” of reactions to the concept of test driven development (TDD) from those not experienced with it: willing students, healthy skeptics and reactionary curmudgeons. The first group is basically looking for a chance to practice and needs no convincing. The last group will have to be dragged along, kicking and screaming, and so there’s no persuading them without the threat of negative consequences. It is the middle group that tends to have rational objections, some of which are well-founded and others of which aren’t so much. A lot of the negative reaction from this group is the result of reacting to the misconceptions that I mentioned in this post about what TDD is and isn’t. But even once they understand how it works, there are still some fairly common and legitimate objections that are not simply straw man arguments.

  1. The most common and prevalent objection is that coding this way means that you’re doing a lot more work. You’re taking more time and writing more code and people don’t necessarily see the benefit, especially in cases where they already know what code they want to write.
  2. Many of the misconception objections and other inexplicable resistance is really the result of people simply not knowing how to write tests or practice TDD, and perhaps at times being reluctant to admit it. Others may freely admit it. Either way, the objection is that TDD, like any other discipline, would take time to learn and require an investment of effort.
  3. There is also more code that is going into a project since you now have an additional test class for each single class you would otherwise have created. More code means more maintenance time and effort.
  4. Many astute observers also realize that a lot of legacy code, particularly that involving large-work constructors, singletons, and static state is very hard to test, making attempts to do so effort-intensive.
  5. And, along the same lines , they also realize that there would be more effort required than simply learning how to do TDD – it would also mean learning different design techniques such as dependency injection, polymorphism, and inversion of control.

When you consider all of these objections, they all have a common thread. At the core of it, they’re really all variants on the theme of not having enough time. Writing the tests, maintaining the test code, learning new ways of doing things, and applying them to new and old code are all things that take time, and for most developers, time is precious. Someone selling TDD is a lot like someone selling you on a 401K: they’re convincing you that sacrificing now is going to be worth it later and asking you to take this, to some degree, on faith.

Could TDD be better?

Justifying the adoption of TDD to a healthy skeptic hinges largely on demonstrating that it provides a net benefit in terms of time, and thus cost. So how can these objections be reconciled and the concerns addressed?

Well first up are the learning curve oriented objections. And the truth is that there’s no way around this one being a time sink. Learning how to do TDD and learning how to write testable code are going to take time, no matter what. If you do not have the time to learn, this is a perfectly valid objection, but only in the shorter term. After all, we work in an industry where change is the only constant and learning new languages, frameworks, and methodologies is pretty much table stakes for staying relevant.

Regarding development time overall, a very common argument made by TDD proponents is that the practice saves time over the long haul. This is reminiscent of the parable of the tortoise and the hare where the TDD practitioner is a tortoise plodding along, getting everything right and the hare is generating reams of code quickly but with mistakes. The hare will declare himself done more quickly, but he’ll spend a lot more time later troubleshooting, reading log files, debugging, and fixing errors. The tortoise may not finish as quickly, but when he does, he truly is done.

But what about in the short term? Is there anything that can be done to make things go more quickly in the short term for TDD practitioners? Could we strap a rocket pack to the tortoise and make him go faster than the hare while preserving his accuracy?

RocketTurtle

Speeding up the Feedback Loop

What if I told you a story? What if I told you that you could write code and know whether or not it was working nearly instantaneously? In this world of development, you don’t have to wait while your application starts up, and then navigate through various user interface screens to get to the action that will trigger the bit of code you want to verify. There is no more repetitive clicking and typing and waiting for screens to load. In fact, in this world you don’t even need to build your project or compile your code. All you need to do is type and see, as you’re typing, whether or not the changes you’re making are right. And, you can see a visual metric for how much confidence you can have in your changes by virtue of how much your code is covered by the unit tests.
Does that story sound too good to be true? Well, I’ll admit that it does sound pretty good, but I’ll let you in on a little secret – it is true. There is a name for this paradigm, and it’s called “continuous testing.” And there are various tools out there for different platforms that make it a reality, right now as we speak.

To understand the magic of continuous testing, it’s essential to understand one of the most important, but often overlooked, concepts in computer science. I’m talking about the feedback loop. At its core, programming is a series of experiments. Whenever you approach a programming task, you have a code base that does something, and you have a goal to make it do something different or new. To achieve this goal, you identify intermediate behaviors that you’d like to see to mark progress, and then you make changes that you think will result in those behaviors. Then, you run the application to see if what you thought would happen does, in fact, happen.

For example, perhaps you want to have your application display customer information stored in a database to the screen when the user clicks a certain button. You might first say “forget the database – let’s just get the button click to result in some hard-coded value being displayed,” and then set about altering the code to make that happen. When you’d made your changes to the code, you’d run the program and click that button to see what had happened.

Considered closely, this process is actually a lot like the scientific method. For step (1) you read the code. For step (2) you hypothesize what you’ll need to do to the code. For step (3) you predict the outcome of your changes, and for step (4) you make the changes and observe the results. The amount of time that it takes to perform an iteration of your coding version of the scientific method is what I’m calling the “feedback loop.” How long does it take for you to have an idea, implement it, and verify that it had the desired effect?

Scientist

In the early days of programming when the use of punch cards was common, feedback times were very lengthy. Programmers would reason carefully about everything that they did because feedback times were extremely slow, meaning mistakes were very costly. While many improvements have been made across the board to feedback times, situations persist to this day when the feedback loop is excruciatingly slow. This includes long running or resource-intensive applications and distributed systems with high latency. With such systems, programmers on projects often devise schemes to try to shorten the feedback loop, such as mocking out bottlenecks to allow fast verification of the rest of the system.

What they’re really trying to do is shorten the feedback loop to allow themselves to be more productive. When a great deal of time elapses between trying something and seeing what happens, attention tends to wander to distractions like twitter or reddit, exacerbating the inefficiency in this already-slow process. Developers innately understand this problem and are frustrated by the long build and run times of behemoth and slow-running applications.

To combat this problem, developers intuitively favor faster schemes. Ask yourself whether you prefer to work on a small project that builds quickly or a large one. How about a slow test suite versus a fast one? By speeding up the feedback loop you trade frustration and wandering attention span for engagement and a feeling of accomplishment. Techniques like relying on fast-running unit tests and keeping modules small and decoupled help a great deal with this, but we can get even faster.

If short feedback is good, immediate is definitely better. Anyone who has done extensive work at a command line or, in general used a Read-Evaluate-Print-Loop (REPL) understands this. Attention does not wander at all during a session like this. Historically, such a thing wasn’t possible in a compiled language, but with the advent of multicore systems and increasingly sophisticated compiler technology, times are changing. It is now possible to have a build running in the background of an IDE like Visual Studio even as you modify the code.

NCrunch

If you’ve been watching my series on building a chess game using TDD you couldn’t help but notice the red and green dots on the left side of the code window, since they catch the eye. What you were seeing was the tool, NCrunch, in action. Now it’s time to get properly acquainted.

NCrunch is a software product by Remco Software and was written by software developer Remco Mulder, who owns the company. It is a tool written specifically to allow developers to practice continuous testing in Visual Studio. NCrunch is a commercial product with a tiered pricing model and full-blown customer support. And it operates as a plugin to Visual Studio so there is no need to integrate or operate any kind of standalone application. It drops right in, comfortably with a tool with which you are already familiar.

For the first several years of its existence, NCrunch was free, since it was in an extended state of Beta release. During the course of these years, it grew a substantial and loyal user base. In the fall of 2012, Remco decided to issue version 1.0 and release NCrunch as a full, commercial product with a licensing model and production support. It is now on version 2.5 and is most certainly an excellent, commercial-grade product that is worth every penny.

As I write my code using this tool, you may notice things that I rarely or never do. I rarely, if ever, run an application. I rarely, if ever, use the unit test runner. I rarely even compile my code, though I do this sometimes simply because I happen to be quite accustomed to looking at compiler feedback in the errors window. Continuous testing tools like NCrunch may have been a novelty when they came out, but I would argue that they’re rapidly becoming table stakes for efficient development these days.

Before NCrunch, the viability of TDD for me was tied up in the idea that investing extra time up front meant that I wouldn’t later be revisiting my code, debugging, tweaking, fixing, when I was further removed and it’s more time consuming. With NCrunch, I don’t even need to make that case. Now, if you took TDD and NCrunch away, my development process would be substantially slower as I sat there waiting for the application to compile or the test runner to do its thing.

If you don’t have this, get it. You won’t be sorry. Forget clean code, unit testing, TDD, all of that stuff (well, not really — but indulge me here for a second). Just get this setup for the tight feedback loop alone. There is nothing like the feeling of productivity you get from typing a line of code and knowing in less than a second, without doing anything else, whether the change is what you want. That incredible power makes it all worth it — the learning curve of the tool, the cost of the tool, adopting TDD, learning to unit test. It’s like getting a car with 500 horse power and feeling that acceleration; it ruins you for anything less.

By the way, if you liked this post and you're new here, check out this page as a good place to start for more content that you might enjoy.

By

Intro to Unit Testing 9: Tips and Tricks

This, I’ve decided, is going to be the penultimate post in this series. I had originally targeted a 9 part series, finishing up with a “tips and tricks” post, the way you often see with technical books. But I think I’m going to add a 10th post to the series where I make the business case for unit testing. That seems like a more compelling wrap up to the series. So, stay tuned for one more after this.

This post is going to be a collection of tips and tricks for new and veteran unit testers.

Structure the test names for readability beyond just descriptive names.

The first step in making your unit tests true representation of business statements is to give them nice, descriptive names. Some people do this with “given, when, then” verbiage and others are simply verbose and descriptive. I like mine to read like conversational statements, and this creates almost comically long names like “GetCustomerAddress_Returns_Null_When_Customer_Is_Partially_Registered.” I’ve been given flack for things like this during my career and you might get some as well, but, do you know what? No one is ever going to ask you what this test is checking. And if it fails, no one is ever going to say “I wonder why the unit test suite is failing.” They’re going to know that it’s failing because GetCustomerAddress is returning something other than null for a partially registered customer.

When you’re writing unit tests, it’s easy to gloss over names of the tests the way you might with methods in your production code. And, while I would never advocate glossing over naming anywhere, you especially don’t want to do it with unit tests because unit test names are going to wind up in a report generated by your build machine or your IDE where as production methods won’t unless you’re using a static analysis tool with reporting, like NDepend.

But it goes beyond simply giving tests descriptive names. Come up with a good scheme for having your tests be as readable as possible in these reports. This is a scheme from Phil Haack that makes a lot of sense.
I adopted a variant of it after reading the post, and have been using it to eliminate duplication in the names of my unit tests. This consideration of where the test names will be read, how, and by whom is important. I’m not being more specific here simply because how you do this exactly will depend on your language, IDE, testing framework, build technology etc. But the message is the same regardless: make sure that you name your tests in such a way to maximize the value for those who are reading reports of the test names and results.

Create an instance field or property called “Target”

This one took a while to grow on me, but it eventually did and it did big time. Take a look at the code below, originally from a series I did on TDD:

[TestClass]
public class BowlingTest
{
	[TestClass]
	public class Constructor
	{

		[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
		public void Initializes_Score_To_Zero()
		{
			var scoreCalculator = new BowlingScoreCalculator();

			Assert.AreEqual(0, scoreCalculator.Score);
		}
	}

	[TestClass]
	public class BowlFrame
	{
		private static BowlingScoreCalculator Target { get; set; }

		[TestInitialize()]
		public void BeforeEachTest()
		{
			Target = new BowlingScoreCalculator();
		}

		[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
		public void With_Throws_0_And_1_Results_In_Score_1()
		{
			var frame = new Frame(0, 1);
			Target.BowlFrame(frame);

			Assert.AreEqual(frame.FirstThrow + frame.SecondThrow, Target.Score);
		}

		[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
		public void With_Throws_2_And_3_Results_In_Score_5()
		{
			var frame = new Frame(2, 3);
			Target.BowlFrame(frame);

			Assert.AreEqual(frame.FirstThrow + frame.SecondThrow, Target.Score);
		}

		[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
		public void Sets_Score_To_2_After_2_Frames_With_Score_Of_1_Each()
		{
			var frame = new Frame(1, 0);
			Target.BowlFrame(frame);
			Target.BowlFrame(frame);

			Assert.AreEqual(frame.Total + frame.Total, Target.Score);
		}

		[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
		public void Sets_Score_To_Twenty_After_Spare_Then_Five_Then_Zero()
		{
			var firstFrame = new Frame(9, 1);
			var secondFrame = new Frame(5, 0);

			Target.BowlFrame(firstFrame);
			Target.BowlFrame(secondFrame);

			Assert.AreEqual(20, Target.Score);
		}

		[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
		public void Sets_Score_To_25_After_Strike_Then_Five_Five()
		{
			var firstFrame = new Frame(10, 0);
			var secondFrame = new Frame(6, 4);

			Target.BowlFrame(firstFrame);
			Target.BowlFrame(secondFrame);

			Assert.AreEqual(30, Target.Score);
		}
	}

	public class BowlingScoreCalculator
	{
		private readonly Frame[] _frames = new Frame[10];

		private int _currentFrame;

		private Frame LastFrame { get { return _frames[_currentFrame - 1]; } }

		public int Score { get; private set; }

		public void BowlFrame(Frame frame)
		{
			AddMarkBonuses(frame);

			Score += frame.Total;
			_frames[_currentFrame++] = frame;
		}

		private void AddMarkBonuses(Frame frame)
		{
			if (WasLastFrameAStrike()) Score += frame.Total;
			else if (WasLastFrameASpare()) Score += frame.FirstThrow;
		}

		private bool WasLastFrameAStrike()
		{
			return _currentFrame > 0 && LastFrame.IsStrike;
		}
		private bool WasLastFrameASpare()
		{
			return _currentFrame > 0 && LastFrame.IsSpare;
		}
	}
}

If you look at the nested test class corresponding to the BowlFrame method, you’ll notice that I have a class level property called Target and that I have a method called “BeforeEachTest” that runs at the start of each test and instantiates Target. I used to be more of a purist in wanting all unit test methods to be completely and utterly self-contained, but after a while, I couldn’t deny the readability of this approach.

Using “Target” cuts out at least one line of pointless (and repetitive) instantiation inside each test and it also unifies the naming of the thing you’re testing. In other words, throughout the entire test class, interaction with the class under test is extremely obvious. Another ancillary benefit to this approach is that if you need to change the instantiation logic by, say, adding a constructor parameter, you do it one place only and you don’t have to go limping all over your test class, doing it everywhere.

I highly recommend that you consider adopting this convention for your tests.

Use the test initialize (and tear-down) for intuitive naming and semantics.

Along these same lines, I recommend giving some consideration to test initialization and tear-down, if necessary. I name these methods BeforeEachTest and AfterEachTest for the sake of clarity. In the previous section, I talked about this for instantiating target, but this is also a good place to instantiate other common dependencies such as mock objects or to build friendly instances that you pass to constructors and methods.

This approach also creates a unified and symmetric feel in your test classes and that kind of predictability tends to be invaluable. People often debug production code, but are far more likely to initially contemplate unit tests by inspecting them, so predictability here is as important as it is anywhere.

Keep Your Mind on AAA

AAA. “Arrange, Act, Assert.” Think of your unit tests in these terms at all times and you’ll do well (I once referred to this as “setup, poke, verify.“). The basic anatomy of a unit test is that you setup the world that you’re testing to exist in some situation that matters to you, then you do something, then you verify that what you did produced the result you expect. A real world equivalent might be that you put a metal rod in the freezer for 2 hours (arrange), take it out and stick your tongue on it (act), and verify that you can’t remove your tongue from it (assert).

If you don’t think of your tests this way, they tend to meander a lot. You’ll do things like run through lists of arguments checking for exceptions or calling a rambling series of methods to make sure “nothing bad happens.” This is the unit test equivalent of babbling, and you don’t want to do that. Each test should have some specific, detailed arrangement, some easily describable action, and some clear assertion.

Keep your instantiation logic in one place

In a previous section, I suggested using the test runner’s initialize method to do this, but the important thing is that you do it, somehow. I have lived the pain of having to do find and replace or other, more manual corrections when modifying constructors for classes that I was instantiating in every unit test for dozens or even hundreds of tests.

Your unit test code is no different than production code in that duplication is your enemy. If you’re instantiating your class under test again and again and again, you’re going to suffer when you need to change the instantiation logic or else you’re going to avoid changing it to avoid suffering, and altering your design to make copy and paste programming less painful is like treating an infected cut by drinking alcohol until you black out and forget about your infected cut.

Don’t be exhaustive (you don’t need to test all inputs — just interesting ones)

One thing I’ve seen occasionally with people new to unit testing and especially getting their heads around TDD is a desire to start exhaustively unit testing for all inputs. For instance, let’s say you’re implementing a prime number finder as I described in my Pluralsight course on NCrunch and continuous testing. At what point have you written enough tests for prime finder? Is it when you’ve tested all inputs, 1 through 10? 1 through 100? All 32 bit integers?

I strongly advise against the desire to do any of these things or even to write some test that iterates through a series of values in a loop testing for them. Instead, write as many tests as you need to tease out the algorithm if you’re following TDD and, in the end, have as many tests as you need to cover interesting cases that you can think of. For me, off the top (TDD notwithstanding), I might pick a handful of primes to test and a handful of composite numbers. So, maybe one small prime and composite and one large one of each that I looked up on the internet somewhere. There are other interesting values as well, such as negative numbers, one, and zero. I’d make sure it behaved correctly for each of these cases and then move on.

It might take some practice to fight the feeling that this is insufficient coverage, but you have to think less in terms of the set of all possible inputs and more in terms of the set of paths through your code. Test out corner cases, oddball conditions, potential “off by one” situations, and maybe one or two standard sorts of inputs. And remember, if later some kind of bug or deficiency is discovered, you can always add more test cases to your test suite. Your test suite is an asset, but it’s also code that must be maintained. Don’t overdo it — test as much as necessary to make your intentions clear and guard against regressions, but not more than is needed.

Use a continuous testing tool like NCrunch

If you want to see just how powerful a continuous testing tool is, check out that Pluralsight video I did. Continuous testing is game changer. If you aren’t familiar with continuous testing, you can read about it at the NCrunch website. The gist of it is that you get live, real-time feedback as to whether your unit tests are passing as you type.

Let that sink in for a minute: no running a unit test runner, no executing the tests in the IDE, and not even any building of the code. As you type, from one character to the next, the unit tests execute constantly and give you instantaneous feedback as to whether you’re breaking things or not. So, if you wander into your production code and delete a line, you should expect that you’ll suddenly see red on your screen because you’re breaking things (assuming you don’t have useless lines of code).

I cannot overstate how much this will improve your efficiency. You will never go back once you get used to this.

Unit Tests Instead of the Console

Use unit tests instead of the console or whatever else you might use to do experimentation (get comfortable with the format of the tests). Most developers have some quick way of doing experiments — scratchpads, if you will. If you make yours a unit test project, you’ll get used to having unit tests as your primary feedback mechanism.

In the simplest sense, this is practice with the unit test paradigm, and that never hurts. In a more philosophical sense, you’re starting to think of your code as a series of entry points that you can use for inspection and determining how things interact. And that’s the real, longer term value — an understanding that good design involves seams in the code and unit tests let you access those seems.

Get familiar with all of the keyboard shortcuts

Again, this is going to vary based on your environment, but make sure to learn whatever keyboard shortcuts and things your IDE offers to speed up test management and execution. The faster you are with the tests, the more frequently you’ll execute them, the more you’ll rely on them, and the more you’ll practice with them.

Your unit test suite should be a handy, well-worn tool and a comfortable safety blanket. It should feel right and be convenient and accessible. So anything you can do to wear it in, so to speak, will expedite this feeling. Take the time to learn these shortcuts and practice them — you won’t regret it. Even if you have a continuous testing tool, you can benefit from learning the most efficient way to use it. Improvement is always possible.

General Advice

Cliche as it may sound and be, the best tip I can give you overall is to practice, practice, practice. Testing will be annoying and awkward at first, but it will become increasingly second nature as you practice. I know that it can be hard to get into or easy to leave by the wayside when the chips are down and you’re starting at a deadline, but the practice will mitigate both considerations. You’ll grow less frustrated and watch the barriers to entry get lower and, as you get good, you won’t feel any inclination to stop unit testing when the stakes are high. In fact, with enough practice, that’s when you’ll feel it’s most important. You will get there with enough effort — I promise.