Drinking is one of my hobby. I love drinking with friends. For the past 10 years, I have been drinking at least weekly. It brings both joy and pain. Joy when I drink, pain the days after. No matter what I still love it.
I have had many terrible hangover days. Each time, I swore to make something different.
Welcome to my biggest challenge:
30 days without alcohol. No exception!
Why?
Experiment! Challenge! The joy of discipline.
I want to optimize my time. Hangover costs too much.
I want to know how my life goes without me spending time on drinking. I want to feel the pain, resistance of discipline. I want to enjoy the winning.
I want them all.
You know nothing unless you try. To win, you have to challenge yourself, have to take action.
What If
What if you fail, my inner voice asks? I do not know. I have to try and make sure I will not fail. If fail, I will start over again. Never give up!
What if after 30 days you drink 30 days to make up the lost? I do not know. But I am sure I am smarter than that.
No matter “what if”, I start it and follow it. In the end, I will feel better. That, I do know!
“What if” is the question that hold you back. To overcome it, I simply say: I do not care, I do not know.
Who With Me?
My friends, you have your own desires. Why don’t you give them a trial of 30 days?
At the end of 30th, you will have something to say
Yeah. I made it
Hmm. I fail. Will start it again
Oh. I did nothing. I just spent 30 days out of my life
We all suffer from 2 things the joy of disciplines or the pain of regrets – Jim Rohn.
It was such an interesting question when he asked: Why do you have this statement?
He made a good point. I promised to give him my thoughts on it.
Things have no meaning unless you label them.
“What UT is” does not matter much. What you think it is matters.
Unit Test (UT) is Code
First and foremost, UT is code. We write a piece of code to test our production code. When comes to code, we have to
Design
Consider best practices
Consider maintainability
Decide what tools to use
… Thousands of other things
As a developer, I say: Code is an Art.
Point of Views
When working in an organization, when talking about Unit Test, each level has a different view. I will give some of my observations. Please notice that I do not claim anything here. I give my observations.
Non – Technical Manager
He assumes UT is a piece of cake, that we should write UT when there is time.
Problem: There is always NO time.
Half – Technical Manager
Maybe he knows some about UT. He knows the important of UT. He also knows the difficulty of writing UT. Regardless of those he still wants to push for code coverage, and think UT is kind of easy.
Developer
I have not seen many developers have the right mindset about UT. And depending on the technical skill, their UT code quality is less than or equal the production code.
I have not seen many developers study UT seriously. To them, it is a nice-to-have thing.
Because of that mindset, their UT code is a maintenance nightmare. Think about when you have to change your production code; when you have to add new code.
Some developers think UT is a joke, a waste of time.
QA
Here is a funny part. Sometimes I heard a story that in a company the manager expects QA to write UT. Oh God!
Many parties just forgot (or pretended to) the fact that UT is code.
When people come to an Art gallery, looking at the same picture. Will they say the same thing? I do not think so.
So What?
People look at things depending on their point of views in life. The same event two people might have different opinions. Sometimes, in order to improve something, we simply need to change our view.
Who should change their view? Developers, in my opinion. Developers get the best advantages out of UT.
Here are a few things I suggest you, developers, to begin with
Do a serious research about UT: What is it? What is it for? What are the best resources to learn it? …
Start today build the habit of writing UT. The key word is TODAY.
After a week, review all UT you wrote. You can ask an experienced developer to give you advice.
And you should not
Study “How to” books at the beginning. “How to” are good only when you know what and why.
Start TOMORROW.
Final Thought
What you think about UT will determine how good you are as a developer. When reading this post, some might think of TDD. But NO. I am not talking about TDD. I highly recommend you start small.
By no mean, I am a UT expert. I am just trying to make things better every day.
Unit Test is an art. There is no silver bullet for what to test, how to test, what tools to use. In my opinion, everything depends on the kind of projects, the maturity of the projects, and the skill of developers in the projects.
The Story
Given this piece of code. It is not a real production code nor production quality code. I made it up to demonstrate in this post.
public class Account
{
public virtual bool IsClosed { get; set; }
public virtual decimal Amount { get; set; }
public virtual void Withdraw(decimal? amount)
{
Amount -= amount.Value;
}
}
public class AccountManager
{
private readonly Account _account;
public AccountManager(Account account)
{
_account = account;
}
public void Withdraw(decimal? amount)
{
if (_account.IsClosed)
return;
if (amount == null)
throw new ArgumentNullException(nameof(amount));
_account.Withdraw(amount);
}
}
This is not a design or best practice post. Let’s focus on the business logic and what/how to test.
There is an Account with 2 properties:
IsClosed (true/false): If closed, cannot withdraw money.
Amount (decimal): The current total amount left in the bank Account. The amount is reduced by the Withdraw method.
I keep the Account class pretty simple.
To manage an account, we have AccountManager. What it does are
If the account is closed, nothing happens.
If not closed, and the amount is null (to demonstrate the point that it is invalid withdrawn amount), throw ArgumentNullException.
If all is valid, the account’s amount is reduced by the withdrawn amount.
How are we going to test that 3 expected behaviors?
The Analysis
I believe at this phase, we have to depend on the mind of the developer. First, we need to recognize what kind of test we should perform.
#1: Look like an interaction test. We want to verify a certain behavior: Nothing happens.
#2: Verify input valid which expects an exception to be thrown.
#3: Look like a state test. We want to verify the account’s state. Why? Because we do not really care how the money is withdrawn. We only care about the amount left.
The Technology
Let bring in the tools we know to test. At this phase, depend on organizations; size and kind of projects. I work on .NET stack. I will use MS Test + RhinoMock (for interaction test).
#1: When the account is closed, nothing happens.
The account class might have many properties (now or later), but I only care about “IsClosed” property.
[TestClass]
public class AccountManagerTest
{
[TestMethod]
public void Withdraw_AccountClosed_DoNothing()
{
// Arrange
var account = MockRepository.GenerateMock<Account>();
account.Expect(x => x.IsClosed)
.Return(true);
account.Expect(x => x.Withdraw(Arg<decimal?>.Is.Anything))
.Repeat.Never();
var sut = new AccountManager(account);
// Act
sut.Withdraw(null);
// Assert
account.VerifyAllExpectations();
}
}
The main point is at the “Arrange” phase. I expect these behaviors
The “IsClosed” must be called
The “Withdraw” must not be called. It means: Do nothing.
I am happy to see that test passed.
#2: Throw ArgumentNullException
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Withdraw_AccountNotClosedAndAmountNull_ThrowArgumentException()
{
// Arrange
var account = new Account {IsClosed = false};
var sut = new AccountManager(account);
// Act
sut.Withdraw(null);
// Assert
}
I can use either Mock or real object at this test as far as ensuring the IsClosed is false.
#3: If all is valid, account’s amount is reduced by the withdrawn amount
[TestMethod]
public void Withdraw_AllValid_AmountReducedByWithdrawnAmount()
{
// Arrange
var account = new Account
{
IsClosed = false,
Amount = 100
};
var sut = new AccountManager(account);
// Act
sut.Withdraw(30);
// Assert
Assert.AreEqual(70, account.Amount);
}
At this test, I do not care how the system implemented. Given a valid account, and a valid amount to withdraw, my account must have the right amount left.
Be very careful, fellows. If you use mock here, you will end up with interaction test.
As I said in the beginning, unit test is an art. You have to research, learn about it every day. Watch other people do it. Make up your own mind. During my career, I found out that developers have a tendency to jump directly to “The Technologies” phase. “The Analysis” phase is very important.
A while ago, I wrote about refactoring 6K LoC class The main benefit of such a refactoring is to have a maintainable codebase.
Last week, I fixed a small bug. The bug itself is trivial. However, the way I enjoyed the new code and wrote unit test was more fun to share. I called it a beautiful solution. And I loved that work.
The Story
In the Refactoring post, I introduced the School and Student to demonstrate the problems. One day, I got a bug report (speak in the blog language, not my real project language)
When removing a student by student’s id, an exception was thrown if the id is invalid.
The expected behavior is not throwing an exception if id is invalid (or not found). It is just a normal business rule in the application.
To demonstrate the story here is the simplest code
public class School
{
protected internal IList<Student> _students = new List<Student>();
private readonly StudentManager _studentManager;
public School()
{
_studentManager = new StudentManager(this);
}
public virtual void AddStudent(Student student)
{
_studentManager.Add(student);
}
public virtual Student FindStudent(Guid id)
{
throw new ArgumentException($"The student you are looking for does not exist {id}", nameof(id));
}
public virtual void Remove(Student student)
{
_students.Remove(student);
}
}
public class Student
{
public virtual Guid Id { get; protected set; }
}
And the consumer code (a simple console application)
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Welcome to the test");
try
{
var school = new School();
var studentId = Guid.NewGuid();
var student = school.FindStudent(studentId);
school.Remove(student);
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.WriteLine("Press key to exit.");
Console.Read();
}
}
Once you run the code
The Manager
Remember that School is a ghost class. And the student management is delegated to StudentManager. Any attempt to fix directly in School class is a big risk.
There is a small issue in the consumer code. The client has to do 2 things
Find a student by id
Ask School to remove that student
They are too much for a client to know and make decisions. What the client wants is the ability to remove a student by id without throwing an exception.
public class StudentManager
{
private readonly School _school;
public StudentManager(School school)
{
_school = school;
}
public void Add(Student student)
{
_school._students.Add(student);
}
public bool TryRemove(Guid studentId)
{
var student = _school._students.FirstOrDefault(x => x.Id == studentId);
if (student == null)
return false;
return _school._students.Remove(student);
}
}
The StudentManager will take care of the logic.
The Client will be happy
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Welcome to the test");
try
{
var school = new School();
var studentId = Guid.NewGuid();
var studentManager = new StudentManager(school);
var result = studentManager.TryRemove(studentId);
Console.WriteLine($"Are you removed? {result}");
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.WriteLine("Press key to exit.");
Console.Read();
}
}
Why? Because it just needs to ask StudentManager to handle the logic (in a single method call) and deal with result in its own way.
The Unit Test
So far I am very happy with the fix without touching the existing giant class (School class). Good!
Good! How am I going to test this implementation?
The system data access layer uses NHibernate which requires all domain properties virtual. That design brings us a big benefit in term of unit testing. We can either mock or substitute objects.
Due to the complexity of the legacy code, I tended to use Mock (which is an interaction test) rather than Substitute (which is a state test).
Ok, what do I mean by Interaction Test and State Test in this example? We will test the StudentManager.TryRemove method.
What To Test?
From the business logic, we will test at least 2 things
A student is removed if the student id is valid.
If a student id is not valid, do not throw an exception.
How To Test?
The #2 logic should be simple to test. This code will ensure that
[TestClass]
public class StudentManagerTests
{
[TestMethod]
public void TryRemove_InvalidStudentId_NotThrowException()
{
// Arrange
var school = new School();
var sut = new StudentManager(school);
// Act
var result = sut.TryRemove(Guid.NewGuid());
// Assert
Assert.IsFalse(result);
}
}
However, #1 is not straight away depending on the mind of the developer. Here are at least 2 ways to think about it
The implementation has to find a student, then remove him if found. Keywords here are the 2 verbs (find and remove). Some might refer to verify those 2 actions invoked. That is the interaction test.
The other might simply think: it does not matter what it does (the implementation) as far as a student is removed. Before the school has 10 students. After invoking the method, it has exactly 9 students, and cannot find that student in the school anymore. That is the state test.
I would go for the state test.
Everything should be easy if all properties are public. But that is not the case. School holds an internal protected student list. Student has a protected Id setter. And we do not want to expose them as public.
The key is to Substitute them. In the Unit Test we create derived classes and override the method we want to hook in or bypass.
Prepare school and student substituted.
internal class StudentSchool : School
{
public StudentSchool(IEnumerable<Student> students)
{
_students = students.ToList();
}
}
internal class IdStudent : Student
{
public IdStudent(Guid id)
{
Id = id;
}
}
They are internal (only visible in the unit test assembly).
Without any change to the production code, I have tests covered
internal class StudentSchool : School
{
public StudentSchool(IEnumerable<Student> students)
{
_students = students.ToList();
}
public int StudentCount => _students.Count;
public bool Exists(Guid studentId)
{
return _students.Any(x => x.Id == studentId);
}
}
internal class IdStudent : Student
{
public IdStudent(Guid id)
{
Id = id;
}
}
[TestClass]
public class StudentManagerTests
{
[TestMethod]
public void TryRemove_InvalidStudentId_NotThrowException()
{
// Arrange
var school = new School();
var sut = new StudentManager(school);
// Act
var result = sut.TryRemove(Guid.NewGuid());
// Assert
Assert.IsFalse(result);
}
[TestMethod]
public void TryRemove_ValidStudentId_CountReduce1AndCannotFindAgain()
{
// Arrange
var batman = new IdStudent(Guid.NewGuid());
var superman = new IdStudent(Guid.NewGuid());
var spiderman = new IdStudent(Guid.NewGuid());
var school = new StudentSchool(new[] {batman, spiderman, superman});
var sut = new StudentManager(school);
// Act
var result = sut.TryRemove(superman.Id);
// Assert
Assert.IsTrue(result);
Assert.AreEqual(2, school.StudentCount);
Assert.IsFalse(school.Exists(superman.Id));
}
}
Oh yeah! Superman is removed 🙂 Who needs Superman btw 😛
There are pain and joy in refactoring + unit testing code. The more you code with the improvement mindset, the more joy you have. It is a journey where you become better and better after each line of code. Keep going my friends.
In any interview sessions, I usually ask candidates about
Books they read
Famous authors, developers they know
Sites they visit most.
…..
I call them the “virtual library“. Some candidates were confused, surprised by my question. Guess what? Most of their answer were close to nothing. They did not have it. They could not name any books, authors, or developers.
“You said you love technologies. How did you learn new stuff?”, I asked. I got many of these
I learn from doing projects, doing real tasks in my company
When I need something, I google. (Luckily they still remember there is a website called google).
I read some code examples….
Why Did I Ask?
First, I know that books, blogs, sites, … are not the only ways to learn technologies, learn new skills. But they are essential.
The mind is a central factory. It controls every aspect of your life including your programming skill. Just like your body, the mind needs food. What is the food of the mind?
Information
Wisdom
Concept
Good thinking habits from others
There are more. But you get the idea. The mind needs food.
The mind needs food.
When I want to hire a developer, I need to know how he fed his mind.
When you come to Google and ask a question, you must know what to ask. And sometimes, you must know why you ask.
Reality
If one consist that they do not need to enrich their virtual library, ask yourselves these questions
How are you doing as a developer?
What is your improvement since last year, last 3 years?
Have you ever asked why a piece of code works the way it works?
Have you ever asked what and how to improve a piece of code?
To many developers, the unit test concept is not new. But how many people actually learn to make a proper unit test? a good unit test? If you are asked to improve your unit test skill, what do you do?
Have a Nice Day
A human being is unique. Our body and mind both need food. You feed them well. They work best for you.
If for some reasons, you have not read books before, you should give it a trial. Pick a technical book (because you are a programmer). Read it.
You cannot know what works for you unless you try it.