Click here to Skip to main content
15,881,172 members
Articles / DevOps / Testing

Software Testing - Best Practices and Principles to Write Unit Testing

Rate me:
Please Sign up or sign in to vote.
4.79/5 (24 votes)
23 Apr 2017CPOL14 min read 26.6K   20   6
Basics of software testing concept, best practices and principles
This article will cover basic testing concept, best practices and principles to write unit testing, how to write test data for unit testing and naming convention using Behavior Driven Development (BDD).

Software Testing

Readers of this topics - Developers, Application Architect.

What Will You Learn from this Topic

We know the difference between the Unit Testing and Integration Testing. But sometimes, we mix Unit Test with Integration Testing. But it is not expected. I will explain the principles and best practice to write unit testing and how to write the test cases using Behavior Driven Development (BDD) during white-box testing. The following concepts will be covered:

  • White-Box and Black-Box
  • Advantages and disadvantages of Unit Testing
  • Principles to Write Unit Testing
  • Best Practice to Write Unit Tests
  • Writing Test Scenario
  • Writing Test Case
  • Writing Test Data
  • Traditional Unit Testing Principle
  • Naming Convention of Test Method
  • Why Need Behavior Driven Development (BDD)
  • No Tight-Coupling For Unit Testing

Basic Overview of the Software Testing

Benefit of Software Testing

  • Reduce risk of failures when systems are transferred to production or live operation
  • Documental proof that business requirements have been met
  • Assurance that users are able to operate designed solution productively
  • Assurance that the system works properly with existing legacy systems

Software Tester - Developer VS. QA

Developer's Point Of View

Developers can verify their implemented logics or codes according to the business requirement. They make sure that all of the logics can run smoothly. On the other hand, they confirm that if end-user passes any un-expected data, then the logics still can handle the exception as well as it can show the proper messages. This is known as White-Box Testing because they can see their internal logic or code. For example, automated unit-testing or automated integration-testing.

QA's Point Of View

Quality Assurance (QA) team can verify the software according to the business requirements. But they don't need to worry about the logic or code. They are testing the functionality as an end-user. They will input the positive data to verify the positive functionality. On the other hand, they will put the wrong data to make sure that they are getting the proper messages for the wrong input. This is known as a Black-Box Testing because they can't see the internal logic or code. They verify the external behaviors. For example, acceptance testing.

Let's Drilldown the Basic Testing Concept

Test Document Basic

  • Test Case – A set of conditions and steps to determine whether a system satisfies the requirements correctly or not. This is described by test data, environment and expected result.
  • Test Suite – a collection of test cases.
  • Test Plan – document describing testing approach, test suites and test cases.
  • Test Strategy – a way to identify test cases from the specification.
  • Test Effectiveness – relative ability of test strategy to find bugs.
  • Test Coverage – the percentage of testable elements that have been tested.

Test Scenarios & Test Cases

If you have to withdraw money from an ATM machine, then it is a scenario. But to withdraw money, you need to execute many test cases.

Test Scenarios - A test-scenario can have multiple test cases. So while starting testing, first prepare test scenarios, then create test cases for each scenario.

Example of the Test Scenario: Checking the functionality of the Login button.

Test-Case - A Test Case is a condition which is executed for expected output with predefined set of steps with known inputs.

Example for Test Cases:

  • Test-Case1: Click on the button without entering user-name and password.
  • Test-Case2: Click on the button only entering user-name.
  • Test-Case3: Click on the button while entering wrong user-name and wrong password, etc.

All of these test-cases will have some expected, unexpected and actual result.

Term of Mistake

  • Error - mistake made by developers during implementation of a software system
  • Failure - incorrect behavior of a program
  • Fault - incorrect code that caused a failure
  • Incident - symptoms associated with a failure
  • Bug - an error or fault
  • Fault Directed testing – finding bugs through failure

Positive Testing and Negative Testing

Positive Testing: When Tester tested the application with valid input/data from positive point of view, then it is known as positive testing.

Negative Testing: When Tester tested the application with invalid input/data from negative point of view, then it is known as negative testing.

White Box And Black Box Testing

Depending on the developers and QA Team, we can say that we have two types of testing. Developers can view the internal logics of the Implemented Application system or development box. This box is visible to the developer and it's called white-Box. Other the hand, the QA team can't see the logical implementation and they can see the functionality of the system as an end user point of view. That's why the box is black and they test the external behaviors. It is called Black-Box Testing.

Advantages And Disadvantages of White Box Testing

Advantages
  • Reveals bugs in hidden code
  • Forces reason in implementation for positive, negative and exceptional cases
  • Get confident and documental proof
  • Improving design and code quality
Disadvantages
  • Expensive compared to time and design
  • Need proper knowledge about testing methodology
  • Cases missed can miss out code

Advantages and Disadvantages of Black Box Testing

Advantages
  • Tester and programmer are independent
  • Testing from user’s point of view
  • Test cases can be prepared right after specification are decided
Disadvantages
  • Duplication of test cases by testers and programmer
  • Only simple testing can be done as all cases can’t be build and tested

Black Box vs. White Box Testing Approach

Black-Box approach
  • Field level check
  • Field level validation
  • User interface check
  • Function level check
White Box approach
  • Statement coverage
  • Decision coverage
  • Condition coverage
  • Path coverage

Type of Testing

  • Unit Testing
  • Integration Testing
  • Functional Testing
  • System Testing
  • Stress Testing
  • Performance Testing
  • Usability Testing
  • Acceptance Testing
  • Regression Testing
  • Beta Testing

Most Common Testing in Many Organizations

Image 1

Granularity Levels

Image 2

  • Unit Test - Verification of single classes
  • Module Test - Testing interaction of groups of classes (package)
  • Integration Test - Verification of interactions between modules/components
  • Functional Test - Verification of external behaviors of modules, components and system
  • System Test - Testing of the system against objectives
  • Acceptance Test - Validation of application against user requirements
  • Regression Test - Re-running all tests on system when it is changed

Spare the Unit Testing

Top 5 Excuses for Not Doing Unit Testing

  1. I don’t have time to do the unit test.
  2. The office pays me to write down the codes, not to write down unit test.
  3. I am supporting a legacy application without unit tests and existing design is not suitable for unit test.
  4. QA and User Acceptance Testing are far more effective in finding bugs.
  5. I don’t know how to write unit test.
Advantages of Unit Test
  • Reduces the level of bugs in production code
  • Saves your development time
  • Automated tests can be run as frequently as required
  • Make easier to change and refactor code
  • Improve the design of code especially with Test-Driven-Development
  • A form of documentation
  • Inspires confidence!
  • Measures of completion
Disadvantages of Unit Test
  • Not implementable to logical operators
  • Insensitive to loop operators (number of iterations)

Best Practice and Principles to Write Unit Testing

Principles to Write Unit Testing

Principle 1. “Test the logic of the class only, nothing else

Note that this is one of the most important principles during unit testing. When you are going to test a class, you should not have dependency on database, file, registry, web services, etc. You should be able to test any class in complete "isolation" and it should be designed to support complete "isolation."

According to this principle, mock out all external services and states and remember unit test NEVER uses-

  • configuration settings
  • a database
  • another Application/Service/file/network I/O
  • logging

Principle 2. “Fail first and set a guard at the door"

Before you write piece of logic, first write your fail test. After that, add or refactor your guard logics and throw proper exceptions or messages for the negative or exceptional data. Finally, run it and pass the test.

Explanation of the Unit Testing Principles

Principle 1: Suppose, you have a method 'IsValidUser' to verify the user name and password for login.

Image 3

Now if you look at the line number 15 and 17, then you will see class 'UserLogIn' has a dependency to the class 'UserDataAccess' and it is tightly coupled. Now the problem is you need to test a method 'IsValidUser' of a class 'UserLogIn' and you have to avoid the dependency. Because if you call 'IsValidUser', then it will call the method 'GetUserInfoByUseName'. According to the unit test principle, if we have a dependency from one method to another outsider class, then we have to mock object for that outsider class. It means that we have to inject some dummy data into the outsider classs-object. So,In this example we need to inject dummy data for the 'GetUserInfoByUseeName(userName). If we do so, then we will able to verify the logics of the method 'IsValidUser' in the 'UserLogIn' class.

So, avoid the tight coupling code and refactor your the code again. Find another one of my articles to know dependency, tight coupling and loose coupling. Now my main goal to make you know the unit testing principle only. Anyway, let's refactor the class.

Image 4

Now the class 'RefactoringUserLogIn' is loosely coupled and you can inject the implemented class of the 'IUserDataAccess' interface which will make your life easy to do unit testing. Note that there are many ways to inject the implemented class. Find another of my articles to know how to mock an object of a class. During Unit Testing, if you mock the 'IUserDataAccess', it means you are implementing a dummy object of 'IUserDataAccess' interface and the method 'IsValidUser' will not able to get any data directly from outside like the database or ORM via 'UserDataAccess' class.

Principle 2

For the first time, more than 50% people fail their road driving test, then practice on the highway, seeking tips from others, they are ensuring the success in the road test. Anyway, If you are already familiar with Test-Driven-Development (TDD), then you are already familiar about the fail test. I will explain TDD to my another article.

Let’s explain the guard logic and how it will help you to pass your unit testing. Say, we have a method ‘GetSumByPositiveNumber’ and it has two parameters. The business requirement is always input the positive numbers and get returns the sum. Never input a negative or zero number.

Image 5

Now according to your business requirement, if you pass the values of parameters like 1 and 2, then the method will return 3 which is correct. But what happens if you pass the negative values as a parameter or some combination like (-5, -1), (-5, 1), (5, 0), (0, 0), etc.?

But if we put some guard logics into the method at the beginning, then the test should be passed. Because we are throwing the exception for the negative data.

Image 6

Best Practices to Write Unit Testing

  1. If old test fails for the extension of the method which is introduced, then remove or enhance old Test and avoid requirement conflict.
  2. If you remove a single line in your tested class and all tests still pass, then you don't have enough unit tests.
  3. Developer will often run the test, so make test easy to run.
  4. Test only public methods and public interface of the component
  5. Don't create instances of classes directly inside a unit test - use factory method.
  6. Refactor the unit test and Keep It Simple. Remember that tests must be maintained by any code - so better to keep it simple.
  7. Any test should run in any order; so avoid dependencies between tests.
  8. Test should be readable like a book; so write comments in Asserts. Write descriptive method names. Use Behavior-Driven-Development (BBD) Technique.
  9. Test everything that could possibly break.
  10. Test everything, but not Private methods

Writing Test Scenario, Test Cases and Test Data

Only 3 Test Cases for Unit Testing, Nothing Else

  1. Positive Test Cases: Correct data to check for correct output
  2. Negative Test Cases: Broken or missing data to check for proper handling
  3. Exception Test Cases: Giving unexpected data or behavior and check for the exception caught properly or not

Test Data According to The Test Cases

Let’s set some test data and consider “GetSum” as an example. Here, my main goal is to show you how we can set some Test Data for the test cases.

Image 7

Types of Test Data

  1. Positive data
  2. Negative data
  3. Exceptional data
Positive Data for Positive Test Cases

The main goal of the positive Test cases is to verify the functionality of the logics.

TestCase-1: Given positive values, should return expected result

  • TestData-1: Set Input Parameters as firstNumber =1, secondNumber=1
Negative Data for Negative Test Cases

The main goal of the negative Test cases, to get proper messages for the bad input according to your business requirements.

TestCase-2: Given invalid values, should produce invalid argument message:

  • TestData-2: Set Input Parameters as firstNumber =-1, secondNumber =-1
  • TestData-3: Set Input Parameters as firstNumber =-1, secondNumber = 1
  • TestData-4: Set Input Parameters as firstNumber = 0, secondNumber = 1
  • TestData-5: Set Input Parameters as firstNumber = 0, secondNumber =-1
  • TestData-6: Set Input Parameters as firstNumber = 0, secondNumber = 0, etc.
Exceptional Data for Exception Test Cases

The main goal of the Exceptional Test cases is to find out the proper exception handling with proper messages. So that your code can’t break for the threshold limits.

Here, you can set the threshold limits of your test data. In DOT NET, the minimum value for a variable of type int is -2147483648 and the maximum value for a variable of type int is 2147483647.

TestCase3: Given threshold limit values, should throw exception message:

  • TestData-7: Set Input Parameters as firstNumber =1, secondNumber =2147483649
  • TestData-8: Set Input Parameters as firstNumber =1, secondNumber = -2147483649
  • TestData-9: Set Input Parameters as firstNumber =2147483649, secondNumber = -2147483649
  • TestData-10: Set Input Parameters as firstNumber =2147483647, secondNumber=2147483647, etc.

Naming Convention Of Test Method For Unit Testing

Traditional Principle of Unit Test

One Test Method is written to test one and only one method and one assert method should test only one expectation at a time.

In short, the principle says – “one function/method and one assert per test method”.

So, let’s consider the below example:

Image 8

Comparing the Traditional Principle to the Real World

Test Scenario

Verify the “GetSum” method.

Test Cases
Positive Test Cases
  • TC1: Given positive values, should return expected result
  • Test Data-1: firstValue =5, secondValue =6
Negative Test Cases
  • TC2: Given zero values, should produce invalid argument message
  • Test Data-2: firstValue =0, secondValue =0
  • TC3: Given negative values, should produce invalid argument message
  • Test Data-3: firstValue =-5, secondValue =-6
Exceptional Test Cases
  • TC4: Given threshold limit values, should throw exception message
  • Test Data-4: firstValue =2147483647, secondValue =2147483647
Test Method Example

Now according to the traditional principle, let’s write the test method for the “GetSum

Image 9

Now according to the traditional principle, we have covered the Positive Test Case with “Test Data-1”. But what about negative and exceptional test cases??

How do we cover the negative and exceptional test cases with the traditional principle??

Behavior Driven Development (BDD)

Why do We Need BDD

If we want to cover all of the behaviors of our test cases according to our previous example, then we need to follow some technique so that we can write down all of the behaviors of the method. So, BDD is the technique which gives us the opportunity to fulfill all of the test cases with standard and readable naming convention. Many people, many minds. There are many techniques to write the naming convention of the test method. But it really depends on you and your preference. There is nothing right or wrong if you follow some other technique. Anyway, in short, we can say that in BDD, components test their expected behavior.

Concept of BDD

  1. Given I am a beginner to the BDD technique, and I have never used this technique before
  2. When I read this tutorial for BDD
  3. Then I started to like it and finally I learn it.

BDD Naming Convention

Test Scenario

Verify the “GetSum” Method

Test Cases
Positive Test Cases
  • TC1: Given positive values, should return expected result
  • Test Data-1: firstValue =5, secondValue =6

Test Method - Naming Convection:

  • GivenPositiveVaidValuesAsParams_WhenGetSumIsCalled_ThenItShouldReturnSumValue

More Readable:

  • Given_Positive_Vaid_Values_As_Params_When_GetSum_Is_Called_Then_It_Should_Return_Sum_Value

Image 10

Negative Test Cases
  • TC2: Given zero values, should produce invalid argument message
  • Test Data-2: firstValue =0, secondValue =0

Test Method - Naming Convection:

GivenZeroValuesAsParams_WhenGetSumIsCalled_ThenItShouldThrowInvalidArgumentException

More Readable -

Given_Zero_Values_As_Params_When_GetSum_Is_Called_Then_It_Should_Throw_Invalid_Argument_Exception

  • TC3: Given negative values, should produce invalid argument message
  • Test Data-3: firstValue =-5, secondValue =-6

Test Method - Naming Convection:

GivenNegativeValues_WhenGetSumIsCalled_ThenItShouldThrowInvalidArgumentException

More Readable -

Given_Negative_Values_When_GetSum_Is_Called_Then_It_Should_Throw_Invalid_Argument_Exception

Exceptional Test Cases
  • TC4: Given threshold limit values, should throw exception message
  • Test Data-4: firstValue =2147483647, secondValue =2147483647

GivenMaxLimitValuesOfIntAsParams_WhenGetSumIsCalled_ThenItShouldThrowSumException

More Readable -

Given_Max_Limit_Values_Of_Int_As_Params_When_GetSum_Is_Called_Then_It_Should_Throw_Sum_Exception

History

  • 12th April, 2017: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
United States United States
Lazy software engineer, don’t believe in hard work.

Comments and Discussions

 
SuggestionGuard conditions logic and exceptions Pin
Wiktor Wandachowicz24-Apr-17 22:33
professionalWiktor Wandachowicz24-Apr-17 22:33 
GeneralRe: Guard conditions logic and exceptions Pin
Habibur Rony25-Apr-17 5:54
Habibur Rony25-Apr-17 5:54 
GeneralRe: Guard conditions logic and exceptions Pin
Wiktor Wandachowicz26-Apr-17 2:52
professionalWiktor Wandachowicz26-Apr-17 2:52 
GeneralRe: Guard conditions logic and exceptions Pin
Habibur Rony26-Apr-17 5:37
Habibur Rony26-Apr-17 5:37 
SuggestionSuggestion Pin
Afzaal Ahmad Zeeshan23-Apr-17 4:54
professionalAfzaal Ahmad Zeeshan23-Apr-17 4:54 
GeneralRe: Suggestion Pin
Habibur Rony23-Apr-17 17:34
Habibur Rony23-Apr-17 17:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.