Actionary

A man is valued by his works, not his words!

Domain Name of This Site Will Be Changed Soon

Hi, my dear friends,

I setup zhangliaoyuan.me for two years, now the domain name will be expired soon. But good news is, I have got new domain name with my full name dot com, so after previous zhangliaoyuan.me domain name be expired in 2015/04/19, I’ll use new domain name blog.zhangliaoyuan.com, I’m looking forward to your further supporting. A bundle of thanks for all of you, keep in touch.

./Eric

-EOF-

Misunderstanding of Unit Testing

In software development, unit testing is used for method/function verification. Most of people agree that unit testing helps on software quality improvement, but no many people deep dive. Have you really understand the relationship between unit testing and quality?

Most of people knows while coding a method/function, the corresponding unit testing shall be added as well. But, why? And a lot of people has misunderstanding about unit testing, I’d like to list some ones here.

Misunderstandings

1. Ask newbie to write unit testing

Who shall be the author of unit testing? definitely shall be the coder himself/herself. But the situation likes this: once you finish the function/method implementation, and use a practice called “debug” to verify your method/function works well, then you will think the task is done, any else (unit testing) is optional. If you are key person, another task will be assigned to you, and the unit testing always will be the low prioirty task, and finally, it’s assigned to a newbie who has the competence to take the “low level” tasks.

But unit testing purpose is to facilitate source code change, and acts as safety during coding, in TDD pratice, unit testing is used to drive development. Do you think your coding shall be driven by a newbie, how can you trust the newbie understand the requirement clearly. If you are rock climber, do you trust a newbie install a safety tool for your climbing?

So, the correct pratice shall be experieced person do requirement analysis and write unit testing, let the newbie to implement the function/method, your unit testing case to verify the implementation is okay or not, you take the responsibility to facilitate the source change, you are the safety officer.

2. Expect to find bug through unit testing

As we know, there are two types of testing called: exploratory testing and verification testing. Unit testing is verification testing, it’s used to verify the method/function does fulfill the requirements or not. Unit testing the executable requirement specification of the product method/function.

The correct pratice shall be write the executable requirement as unit testing case, and implement the product method/function untill all of related unit testing cases are passed.

If you would like to find more bug, please make exploratory testing manually, it requires experienced tester, and be more creative.

3. Pursue high coverage rate

I know, there might be a KPI in your team, the unit testing line coverage rate shall be higher 80%, function coverage rate shall be 100%, and branch coverage rate, conditional coverage rate. If you can reach 100% coverage for all of above four kinds of coverage, your manager will thumb up and say “well done”. We spend a lot of time to pursue high coverage rate, did you think that does it worth?

Let’s look below source code

1
2
3
4
5
6
7
8
int foo(int x, int y)
{
    int z = 0;
    if ((x > 0) && (y > 0)) {
        z = x;
    }
    return z;
}

We use assertEquals(2, foo(2, 2)) can touch all line of the code, it means the line coverage rate is 100%, but does it mean the source code has been approven okay, I don’t think so.

Source code metrics somehow likes the weather forecast report indicates, single metric does not make sense, you have to combine all of the metrics, and give you insight. Metrics just tell you there may be something wrong, you need to spend time deep dive in the source code.

Some managers are used to challenge team with metrics, because they read excel daily, but never read your source code.

Josphal has written a blog to introduce the test coverage rate role, above example is from his blog.

4. Write unit testing for legacy code

I really appreciate people starts to care about the legacy code, start to make refactoring, that’s good. But only add unit testing to legacy code without refactoring legacy code is dangerous. There is a manager required his team soruce code (including legacy code) unit testing coverage rate shall reach higher 80%, so team member spent a lot of time to write the unit testing for the legacy code, and tried their best time to cover each line of code, branch, and conditions. The unit testing totally was implemented in white-box, it means if the source code logic change, there will be unit testing case failed.

My question is:

  • Is your legacy code testable?
  • Is your new added unit testing case maintainable?

Your unit testing case will be technical debts as well as the legacy code, trust me. Several years ago, I read a post which is written by Steven Sanderson, called Selective Unit Testing gave a great strategy to handle legacy code unit testing according to the cost and benefit balance. The article shows the legacy code can be identfied four types according to the benefit and cost. As we know, the unit testing implementation cost depends on the source code dependancy, high code dependancy means high unit testing cost:

Alt text

  • Algorithm: This kind of code has complex calculation logic, but lack dependancy, all calculation is depends on the argument input value, and then give out an output. This code is exterem important, any error will lead the program computing failed. So we called it’s high benefit and low cost, the unit testing for this kind of code is required.
  • Trivial: This kind of code has simple logic, sometimes, the method just several LoC, and there is more dependancy with other source code, we call it low benefit and low cost. So we think the unit testing for this code is optional.
  • Coordinator: This code acts as coordinator role, it coordinate methods/functions work, it helps the methods integration, and used to describe a specific functional scenario, for example, the initialization, main etc. It has a lot of dependancy with other source code, because it needs to invoke other functions/methods. We call it high cost, but low benefit, because the logic is simple. A lot of dependancies means you need to stub or mock, that’s high cost. For legacy code, we won’t make unit testing for it. Instead, we use functional testing or module testing to cover the sceniro requirements.
  • Overcomplicated: This kind of code has the characters of Algorithm and Coodinator code, complex logic, and has a lot of dependancies with other code. For this kind of code, we have to refactor it to Algorithm and Coordinator codes, and then based on above strategy to decide to make unit testing or not.

5. Treat unit testing as white-box testing

As academism, the unit testing is white-box testing, but I’m hard to agree that, I suggest people write unit testing as black-box rather than white-box.

If we write unit testing as black-box, in another words, the test cases describe the method/function requirement, as we know, there are kinds of implementations for same one requirement. If the implementation has been changed, the unit testing case still can be used to verify the method/function correct or not. But, if you implement the unit testing in white-box way, once the production code implementation changes (method/function signature is not changed), you have to update your test case as well. How bad.

Unit testing implemention shall be align the requirement, not the implementation logic.

Reference:

-EOF-

Understand the D3js Data Binding

To be honest, it’s not easy to understand the data binding and related DOM element updating via d3js, acutally, it’s a basic of d3js. Here I write down my understanding here, hope it can help some d3js newbie like me.

Basic concepts

Select data/DOM element pair

First of all, if we would like to create a svg picture, we have to create a <svg> DOM element first in html, use below code snippet will implemet it.

1
var svg = d3.select('#chart').append('svg').attr('width', 900);

It means select an exsited element which its id equals chart, and append a child element <svg> under this selected element, give the attribute width equals 900px.

Do you know what’s the meaning of d3.selectAll() during we use d3js? It means there will be several data/element(s) pair(s) be selected. We can image that, in d3js concept, every DOM element has corresponding data, it called data/element pair, like below picture shows:

Alt text

Okay, let’s see what’s happened after below source code be executed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
  <h1>Demo</h1>
  <div id="chart"></div>
    <script type="text/javascript">
      var data = [1, 2, 3];
      var svg = d3.select('#chart').append('svg').attr('width', 900);
      var s = svg.selectAll("rect").data(data);
    </script>
</body>
</html>

After d3.selectAll('rect'), we can image it will return a list of pair which consists of data and elements, but now, the data area is empty. You will ask, how many pairs in the list after only selectAll() executed, and there is no previous <rect> existed? Actually, the number is not identified if only execute this code.

Alt text

After exectue data() methoed, the data and elements have been binded together, the data area will be filled the data, and the pairs number will be identified, here, the data has 3 values, so the there are 3 pairs of data/element pairs, like below picture shows:

Alt text

Now, the pairs has been selected, but the html file, there is no real DOM generated, we can image that, after this code snippet execution, there is only generate pair list in memory, and next, we can finish the html DOM element generating.

Combile the data with element via enter().append(element) method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<script src='http://d3js.org/d3.v3.min.js' charset='utf-8'></script>
</head>
<body>
  <h1>Demo</h1>
    <script type='text/javascript'>
      var data = [1, 2, 3];
      var svg = d3.select('#chart').append('svg').attr('width', 900);
      var s = svg.selectAll('rect').data(data).enter().append('rect');
    </script>
</body>
</html>

enter() method is invoke, we can image the new added binding pairs be selected, the method will return a list of data/unknown object, actually unknown object will be identified by next method append('rect'), in another words, data/htmlElement pairs will be returned by append('rect') method. let’s see what’s difference bewteen enter() and append('rect):

Alt text

And now, we can give the <rect> attributes to draw svg, for example attr('width', 10), attr('x', 0) and attr('y', 0) etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<script src='http://d3js.org/d3.v3.min.js' charset='utf-8'></script>
</head>
<body>
  <h1>Demo</h1>
  <div id='chart'></div>
  <script type='text/javascript'>
        var data = [1, 2, 3];
        var svg = d3.select('#chart').append('svg')
              .attr('width', 900);
          var s = svg.selectAll('rect')
                .data(data)
                .enter()
                .append('rect')
                .style('fill', 'green')
                .attr('width', function(d){return d * 10;})
                .attr('height', function(d){return d * 10;})
                .attr('x', function(d){return d * 100});
  </script>
</body>
</html>

according above code snippet, the html shall be like this.

Alt text

But, if there are 3 <rect> exist, and later we would like to add more according to the data chagne, for exampe: [1,2,3] to [1,2,3,4,5], check below code snippet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html>
<head>
        <script src='http://d3js.org/d3.v3.min.js' charset='utf-8'></script>
</head>
<body>
  <h1>Demo</h1>
  <div id='chart'>
      <svg width='900'>
          <rect width='10' height='10' x='100' style='fill: #008000'></rect>
          <rect width='20' height='20' x='200' style='fill: #008000'></rect>
          <rect width='30' height='30' x='300' style='fill: #008000'></rect>
      </svg>
  </div>
  <script type='text/javascript'>
        var data = [1, 2, 3, 4, 5];
        var svg = d3.select('svg')
              .attr('width', 900);
          var s1 = svg.selectAll('rect');
        console.log(s1);
          var s2 = s1
                .data(data);
        console.log(s2);
        var s3 = s2
                .enter();
        console.log(s3);
        var s4 = s3.append('rect');
        console.log(s4);
               s4.attr("width", function(d){return d * 10;})
                      .attr("height", function(d){return d * 10})
                  .attr("x", function(d){return d * 100})
                  .style('fill', 'red');
  </script>
</body>
</html>

the corresponding html like this:

Alt text

first after selectAll('rect'), return previous existed ones, and data(data) method will bind two more objects, the data mapping with the element by index. enter() method will still keep the length of data/element pair, but only keep the two new added one, others left empty. And next call append('rect'), and append('rect') return the new two objects, coming attribute, style setting are for the new two, that’s why we see the previous three rects are in green color, and new two are in red color.

Remove the no exist data/element pair via exit().remove() method

If there are elements existed, and we would like to remove some ones according to the data has been changed. See below code snippet shows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html>
<head>
        <script src='http://d3js.org/d3.v3.min.js' charset='utf-8'></script>
</head>
<body>
  <h1>Demo</h1>
  <div id='chart'>
      <svg width='900'>
          <rect width='10' height='10' x='100' style='fill: #008000'></rect>
          <rect width='20' height='20' x='200' style='fill: #008000'></rect>
          <rect width='30' height='30' x='300' style='fill: #008000'></rect>
          <rect width='40' height='40' x='400' style='fill: #008000'></rect>
          <rect width='50' height='50' x='500' style='fill: #008000'></rect>
      </svg>
  </div>
  <script type='text/javascript'>
        var data = [1, 2];
        var svg = d3.select('svg')
              .attr('width', 900);
          var s1 = svg.selectAll('rect');
        console.log(s1);
          var s2 = s1
                .data(data);
        console.log(s2);
        var s3 = s2
                .exit();
        console.log(s3);
        var s4 = s3.remove();
        console.log(s4);
  </script>
</body>
</html>

Alt text

svg.selectAll('rect') will return the existed DOM elements, in this case, the previous existed <rect> is 5, and the data(data) methed will combine the data, the data only two data values, after binding, return the objects which mapping previous exist data, here mapping key is the data index. We can make an easy calculation, s1 has 5 objects, and s2 has 2 objects, so the s3 will store 3 objects which is waiting to remove. So later invoke exit().remove() methods will remove the DOM element from html.

Update parttern

According above section experiment, now let’s implement an update implementation here, the DOM element adding and removal according to the data updating.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html>
<head>
        <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
    <h1>Demo</h1>
    Data: <input id="data" type="text" name="data"><br>
    <input type="button" value="Submit" onclick="upate()">
    <div id="chart"></div>
    <script type="text/javascript">
        var svg = d3.select("#chart")
            .append("svg")
            .attr("width", 900)
            .attr("height", 400)
            .append("g")
            .attr("transform", "translate(20, 20)");
        function upate() {
            var inputValue = document.getElementById("data").value;
            data = inputValue.split(",");
            var s = svg.selectAll("rect")
                .data(data).style("fill", "red");
            s.enter()
                .append("rect").style("fill", "green");
            s.exit()
                .remove();
            s.attr("width", function(d){return d * 10;})
                .attr("height", function(d){return d * 10})
                .attr("x", function(d){return d * 100});
        }
    </script>
</body>
</html>

We enther the data via input box, and generate the svg according the data, we can set different color to the existed and new added data, that can clear to know the attributes setting for which objects. The pattern shall be like this:

  • data bind
  • update the existed objects attributes and style etc.
  • enter().append()
  • setting new added objects attributes and style etc.
  • exit().remove()
  • setting all objects attributes and style

Another example, please check the source code which is implemented by my colleague Simon: http://jsfiddle.net/simshi/jmvbcwru/2/

-EOF-