- Prison Code Breaker Diary -

=> aka: Nhật Kí Code Tù

Categories

Showing posts with label C#. Show all posts

Let's start unit-test journey using this simple class, in which we want to test whether this class returns all passes or failures.


using System;
using System.Collections.Generic;
using System.Text;

namespace UnitTest
{
    enum CalculatingType
    {
        Addition,
        Subtraction,
        Multiplication,
        Division
    }
    class Calculator
    {
        private List List;
        private CalculatingType cType;

        public Calculator() { }

        public Calculator(List list, CalculatingType type)
        {
            this.List = list;
            this.cType = type;
        }

        public List CList
        {
            get { return this.List; }
            set { this.List = value; }
        }

        public CalculatingType CType
        {
            get { return this.cType; }
            set { this.cType = value; }
        }

        public double GetResult()
        {
            double result = this.List[0];

            for (int i = 1; i < this.List.Count; ++i)
            {
                if (cType == CalculatingType.Addition)
                    result += this.List[i];
                else if (cType == CalculatingType.Subtraction)
                    result -= this.List[i];
                else if (cType == CalculatingType.Multiplication)
                    result *= this.List[i];
                else
                    result /= this.List[i];
            }

            return result;
        }

    }
}

That's it, there have 'Calculator' class containing a list of double values and a enumeration type indicating what kind of computing. Right now, you should have installed NUnit before moving on. Let's create a new class named 'CalculatorTest' to test our created-class 'Calculator'. So what do we want to test? It can be, and should be: + constructors + properties + methods Since this class is quite simple, so testing constructors and properties can be considered alike; in other words, we will test properties and methods of class 'Calculator' In the current project, add a new class named 'CalculatorTest'. Add Reference to NUnit.Framework.Dll, which is located in installed NUnit directory. First of our test code
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;

namespace UnitTest
{
    [TestFixture]
    class CalculatorTest
    {
        [Test]
        public void TestCListA()
        {
            Calculator calc = new Calculator(new List { 1, 2, 3 }, CalculatingType.Addition);
            Assert.AreEqual(new List { 1, 2, 3 }, calc.CList);            
        }
    }
}

Build project, then call NUnit from VS, click button 'Run'.
You've got a pass! (list picture below)


So far so good, let's go into some explanation.
Since you've added NUnit Framework, you can add attribute to the classes or methods to indicate you want to test them.
To indicate a class test, [TestFixture] is used before the class declaration, and [Test] is used before methods'.
Simple enough?
Try the next test function to test whether our expected result and actual result are the same

        [Test]
        public void TestCListB()
        {
            Calculator calc = new Calculator(new List { 1, 2, 3 }, CalculatingType.Addition);            
            Assert.AreEqual(new List { 2, 3, 1 }, calc.CList);
        }

We've got a fail here
UnitTest.CalculatorTest.TestCListB:
  Expected and actual are both  with 3 elements
  Values differ at index [0]
  Expected: 2.0d
  But was:  1.0d

So that means the numbers at index 0 are different. We may understand like both lists containing the same number but arranged in different order, but in facts, it isn't; they're compared one-by-one iterate through index of each list.

Try a different list
        [Test]
        public void TestCListC()
        {
            Calculator calc = new Calculator(new List { 1, 2, 3 }, CalculatingType.Addition);
            Assert.AreEqual(new List { -1, 2, 3 }, calc.CList);
        }

It's certainly a fail because the first value of each list is different.

Let's test the other property, CalculatingType
        [Test]
        public void TestCTypeA()
        {
            Calculator calc = new Calculator(new List { 1, 2, 3 }, CalculatingType.Addition);
            Assert.AreEqual(CalculatingType.Addition, calc.CType);
        }

        [Test]
        public void TestCTypeB()
        {
            Calculator calc = new Calculator(new List { 1, 2, 3 }, CalculatingType.Addition);
            Assert.AreEqual(CalculatingType.Subtraction, calc.CType);
        }
It's easy to see that TestCTypeA() passes and TestCTypeB() fails logically.

Similarly, we will create test function for the GetResult(). Since there're 4 kind of computing, so we will divide into 4 small test functions to check each kind of computing,

        [Test]
        public void TestAddition()
        {
            Calculator calc = new Calculator(new List { 1, 2, 3 }, CalculatingType.Addition);
            Assert.AreEqual(6, calc.GetResult());
        }


        [Test]
        public void TestSubtraction()
        {
            Calculator calc = new Calculator(new List { 1, 2, 3 }, CalculatingType.Subtraction);                      
            Assert.AreEqual(-4, calc.GetResult());
        }

        [Test]
        public void TestMultiplication()
        {
            Calculator calc = new Calculator(new List { 1, 2, 3 }, CalculatingType.Multiplication);
            Assert.AreEqual(6, calc.GetResult());
        }

        [Test]
        public void TestDivisionA()
        {
            Calculator calc = new Calculator(new List { 1, 2, 3 }, CalculatingType.Division);
            Assert.AreEqual(0.1666667, calc.GetResult());
        }

We have passed at function TestAddition(), TestSubtraction(), TestMultiplication(), but failed at function TestDivisionA().
The reason is the floating point not match. Well, it's kinda of trick here.
Luckily that we have first operation 1/2 = 0.5 limited so we can write like

        [Test]
        public void TestDivisionB()
        {
            Calculator calc = new Calculator(new List { 1, 2, 3 }, CalculatingType.Division);
            Assert.AreEqual( 0.5/3, calc.GetResult());
        }
Good trick! We pass TestDivisionB().
Not happy yet, what if the input for Division containing 0 (zero)?
It is an exception, isn't it?
NUnit provides an attribute to test whether an exception is called out.

        [Test,ExpectedException(typeof(DivideByZeroException))]
        public void TestDivisionC()
        {
            Calculator calc = new Calculator(new List { 1, 2, 0 }, CalculatingType.Division);
            throw new DivideByZeroException();
        }

As you see above, we expect to get an exception type DivideByZeroException, a .NET class.

Software testing is quite a complicated task and consumes a lot of time. Well, this post just demonstrate a very very simple introduction to unit test.
Hope you learn something from this tutorial.

will add NUnit to Visual Studio as an External Tools

Main Menu: Tools > External Tools

Click button 'Add':

Title: NUnit Test GUI
Command: C:\Program Files\NUnit\bin\net-2.0\nunit.exe
Arguments: $(TargetName)$(TargetExt)
Initial Directory: $(ProjectDir)\bin\Debug

Click button 'OK'.



Now you can create your assembly and test it with NUnit tool plugged into your VS.

As far as I know, NUnit is the best Unit Test for .NET, give it a try!
http://nunit.org/index.php?p=download

Programming .NET is one of the first thing I've learned myself long ago, but only available in Windows Operating System.
Somehow, the Mono Project has made .NET Framework become a cross-platform environment that you can freely programming .NET for Windows, Linux, MacOS...in that system directly. Also, you can write a .NET program for Linux under Windows and vice-versa. It's really a great achievement that Mono Project has reached. I expect that Mono Project will achieve more impressive goals with .NET technology in the future as it develops.
I write this guide as a journal in programming .NET under Linux environment by using Mono Project.
You may take this as a reference for Gtk#.

1 - Documentation and Reference:
Here the list that I read and reference from:
Gtk# Tutorial Page by Mono
Mono Documentation Library (equivalent to MSDN)
Mono: A Developer's Notebook (Edd Dumbill, Niel M. Bornstein) - A great book, worth to buy for two reasons:
+ It's the only book on Mono up to this time.
+ It's written as a developer's journal style, so it's really easy to catch the authors' mind concepts.

2 - Programming Gtk# Journal

Lesson 1: Say Hello
Lesson 2: Hello Gtk# (using Form)
Lesson 3: MonoDevelop IDE vs. manual text coding w/ commands. (to be updated...)


Feel free to comment!
Have fun!

Download Project

We do the same thing with Lesson 1 but this time we use form for better GUI.
This is the source code, self-explained fully-commented.



using System;
using Gtk;

public class Lesson_2
{

public static void Main() {

Application.Init();

// create a window
Window window = new Window("Lesson 2: Say hello with Gtk# Form");
window.Resize(400, 40);

// create a label
Label label = new Label("Hello Gtk# .NET");
// add label to the form
window.Add(label);
// show form and its widgets
window.ShowAll();

Application.Run();
}
}


Build and run, this is result:


Have fun!

Download Project: Lesson 1 - Say Hello Gtk#

First, open MonoDevelop and create a new empty project. Then, add an empty class file into project. Copy this code below to the file:


using System;

public class Lesson_1 {

public static void Main() {
Console.WriteLine("Lesson 1: Hello Gtk#! It's so wonderful!");
}
}

If you already know C#, you, hence, know what it means.

Now click on the menu Build -> Build Solution or F8.
Click menu Run -> Run or Ctrl+F5.
You will have result like this:

The result is shown under the Application Output textbox.

However, if you want to compile a single C# file or a C# project manually, then do this.

Open the terminal:

terminal> mcs -pkg:gtk-sharp-2.0 Main.cs

A file named Main.exe will be generated. In order to run this file you have to use mono utility:

terminal> mono Main.exe

The result will show up exactly how it is while using MonoDevelop.

Have fun!