Unit Test

Content

Introduction

  • 1.1. Advantage - Why do we need Unit Test
  • 1.2. Writing criterion
  • 1.3. Annotations
  • 1.4.Lifecycle
  • 1.5.Coverage

Test Strategy

  • 2.1.Write simple logic test case
  • 2.2.Write complex logic test case
  • 2.3.Write I/O test case
  • 2.4.Write queue test case

1.1. Advantage - Why do we need Unit Test

Why do we need Unit Test?

1. To expose latent problems.
Writing an unit test for your function module, class or method properly will avoid some latent bugs which you may not expect at coding stage. Furthermore, a right unit test can point out some inconsistency among functions or modules after you refactor some code.

2. Can be a reference of API.
Once you covered all of public methods, functions, interfaces or modules by unit tests, you or others may refer to these elaborate unit tests as a guidance documentation. One hand they instruct you how to use methods, just like a snippet of sample code, on the other hand, the unit test class tells you what the class which is tested can do.

3. Easy to do regression test.
The unit test can be executed automatically by unit test framework. If you have got these unit tests ready and rational, you can easily go through all of these unit tests by one click. It will save a lot of time and make your work more efficiently.

4. To verify maintainability and scalability of your methods,functions.
If your methods, functions, interface are not so easy to be unit tested, I would say your methods may be hard-to-use. Even you are
not able to write an unit test on your method

1.2. Writing criterion

1: For Package: package name. Such as de.com.medavis.

2: For Class?Test+class or class+Test name. Such as TestCalculator or CalculatorTest.

3: For Method: test+method name. Such as testAdd().

1.3. Annotations

@Before

An initialization method for each test method must perform a (note the difference with BeforeClass, which is executed once for all method).

@After

The release of resources for each test method must perform a (note the difference with AfterClass, which is executed once for all method).

@Test

Test method, can expect to test abnormalities and timeout here.

@Test (expected=ArithmeticException.class)

Detect method will throw a ArithmeticException.

@Test (timeout=1000)

When run this method cost time>1000(ms) this test case is failed.

@Ignore

Test methods ignore.

@BeforeClass

For all test, only once, and must be static void.

@AfterClass

For all test, only once, and must be static void.

@FixMethodOrder(MethodSorters.NAME_ASCENDING)

Sorts the test methods by the method name.

@FixMethodOrder(MethodSorters.NAME_DEFAULT)

Sorts the test methods in a deterministic, but not predictable.

1.4.Lifecycle

这里写图片描述

@BeforeClass and @AfterClass for all tests, run only one times.Use of the database connection, the message queue initialization of some expensive resources.

   /**
     *When getConnection method have some exceptions.
     *Test case  will throw it ,then continue to excute the test method.
     */
    @BeforeClass
    public static void setUpBeforeClass() throws Exception{
        con = ConDB.getConnection();//create connection
    }
    @AfterClass
    public static void tearDownAfterClass() throws Exception{
        con.close();//close connection
    }

1.5.Coverage

The most popular type of coverage:

1: The basic statement coverage to ensure that every statement is executed.

2: Decision coverage to ensure every decision is executed.

3: The condition coverage to ensure that each conditions are covered by the true and false (conditional statements, if while).

4: The path coverage to ensure that every path is executed.

Summary:

1: The coverage of data only represent what you test, These Can not represent the test code is a good unit test.

2: The path coverage > condition coverage > basic statement coverage.

3: Write test cases do not blind pursuit of code coverage. 80% time should be used in the important code.

int foo(int a, int b) {
    return a / b;
}

TestCase

a=10 , b=5, Coverage=100%.

In fact we ignore one case when b=0, it will throw exception.

public testDemo(String a , String b) {
    if (a > b) {
        …
    }
    if (b>c) {
        …
    } else {
        throw new Exception(……);
    }
}

When we want write a test case for this method we need go through these three branches. So we need write four test method.

We need to test every important branches.

**

2.Test Strategy

Class Calculator need test:

package de.com.medavis.cn.junit;


public class Calculator {

    private static int result = 0;

    public void add(int a, int b) {
        result = a + b;
    }

    public void divide(int n) {
        result = result / n;
    }

    public void square(int n) {
        result = n * n;
    }

    //This method just for the timeout test case
    public void loopAdd(int n) {
        while(true) {
            result = result + n;
        }
    }

    public void multiply(int n) {
    }

    public void clear() {
        result = 0;
    }

    public int getResult() {
        return result;
    }
}

This is test class:

public class TestCalculator {    

    Calculator ca = new Calculator();

    @Before
    public void setUp() throws Exception {
        ca.clear();
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void testAdd() {
        Calculator ca = new Calculator();
        ca.add(10, 50);
        assertEquals(60, ca.getResult(), 0);
    }

    @Ignore("Multiply() Not yet implemented")

    @Test
    public void testMultiply() {
    }

    @Test
    public void testDivide() {
        ca.add(8, 6);
        ca.divide(2);
        assertEquals(7, ca.getResult());
    }

    @Test(expected = ArithmeticException.class)
    public void testDivideByZero() {
        ca.add(8, 6);
        ca.divide(0);
        assertEquals(7, ca.getResult());
    }
    /**
     *When this method excute time>1000(ms)?
     *This test case is failed?
     */
    @Test(timeout = 1000)
    public void testLoopToAdd() {
        ca.loopAdd(10);
    }
}

1: Import package

You need to add an important package “org.junit.Assert.*”.

2: The statement of test method

In the test class, not every method are used to test, you must use the “@” to clear what is the test method.

3: A simple test case

You need use @Test labeling, to show that this is a test method. There are requirements for the method statement: the return value must be void,

and should not have any parameters.

4: Runner

How Junit to run this test case?

Junit have a lot of runners , Each runner has its own special function.

import org.junit.internal.runners.TestClassRunner; 
import org.junit.runner.RunWith;

 //Default Runner is TestClassRunner
 public class CalculatorTest ...{
       ...
 }

 //The code above the same function
 @RunWith(TestClassRunner.class)
 public class CalculatorTest ...{
       ...
}

Now use a parameter runner:

There are many special values of parameters, or that the arguments is divided into many regions, we can use this runner.

import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class TestSquare {

    private Calculator calculator = new Calculator();
    private int param;
    private int result;

    @Parameters
    public static Collection data() {
        return Arrays.asList(new Object[][] { {2, 4}, {0, 0}, {-3, 9},});
    }

    public TestSquare(int param, int result) {
        this.param = param;
        this.result = result;
    }

    @Test
    public void test() {
        calculator.square(param);
        assertEquals(result, calculator.getResult());
    }
}

2.2.Write complex logic test case

How to write the test cases for the complex method?

Example:

public static void initRandomMessageList(String parameter) {

        Global.randomDictMap = new HashMap<String, List<String>>();
        //This is a public method
        readfile(parameter);
        //This is a public method
        loadTemplate();

        Global.randomDictMap.clear();
}

There are some operation s of methods , as long as the test results of it, there is no need to return a value.

1:we need have test cases for the method readfile() and the method loadTemplate.

2:we need have test cases for this initRandomMessageList():

 @Test
 public void testInitRandomMessageList(){

     MessageUtil.initRandomMessageList("parameter");

     Assert.assertTrue(Global.randomDictMap.isEmpty());

Use stub and mock to test complex logic test.

What is stub and mock?

The same point: We usually focused on the test object function and behavior, mock or stub can replace the secondary object .

Different points: 1:stub has an explicit class, mock is usually the mock toolkit such as easymock to implicit implementation.

2:Mock usually rarely consider multiplexing, each mock object through the “just enough” is to follow the principles, generally applies only to the current test method.

And stub is generally more convenient to reuse, especially some common stub, such as JDBC connection etc..

3:For mock, we expect the method has not been called, looking forward to the appropriate parameters, the number to call to verify,

and expected in the testing process and after the end of the test. But for stub, usually do not pay attention to this.

This is a demo to the difference between stub and mock.
这里写图片描述

public interface UserService {
    User query(int userId);
}


public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public User query(int userId) {
        return userDao.getById(userId);
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

public interface UserDao {

    User getById(int userId);

}
public class UserDaoStub implements UserDao {

  public User getById(int id) {
        User user = new User();
        user.setId(id);
        user.setUsername("admin");
        user.setPassword("123");
        user.setNickname("amdin");
        return user;
  }

}
public class TestStub {

    @Before
    public void setUp() throws Exception {
    }


    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void testQuery() {
        UserServiceImpl service = new UserServiceImpl();
        UserDao userDao = new UserDaoStub();
        service.setUserDao(userDao);
        User user = service.query(1001);
        assertEquals(user.getId(), 1001);
    }

}
public class TestMock {

    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void testQuery() {
        UserDao dao = EasyMock.createMock(UserDao.class);
        User user = new User(1,"admin","123","admin");
        EasyMock.expect(dao.getById(1001)).andReturn(user);
        EasyMock.replay(dao);
        UserServiceImpl service = new UserServiceImpl();
        service.setUserDao(dao);
        User tu = service.query(1001);
        assertEquals(tu.getId(),user.getId());
        assertEquals(tu.getUsername(),user.getUsername());
        assertEquals(tu.getPassword(),user.getPassword());
        assertEquals(tu.getNickname(),user.getNickname());
        EasyMock.verify(dao);
    }

}

2.3.Write I/O test case

This is a demo to test file loading.

/**
    *Two methods to test this template load
    */
   @Test    
   public void testLoadTemplateFile() {
        String existPath = "ADTTemplate.txt";
        String messageTemplateContent = null;
        try {
            messageTemplateContent = MessageTemplateUtil.loadTemplateFile(existPath);
        } catch(IOException e) {
            fail();
        }
        assertNotNull(messageTemplateContent);
    }

    @Test(expected = IOException.class)
    public void testLoadTemplateFileNotExistPath() throws IOException {
       //This path is not exist, we can catch IOException
        String notExistPath = "ADTTemplateNotExistPath.txt";
        MessageTemplateUtil.loadTemplateFile(notExistPath);
    }

2.4.Write queue test case

Sometimes we need to test the queue. Now This is a demo to show how to test?

This is a ServerSiteWorkStub to simulate a consumer, But the this is a asynchronous transmission and junit don not support this case,

we need use QueueBrower to avoid this asynchronous transmission.

Public class ServerSiteWorkerStub implements MessageListener {

    private ClientToServerJob clientToServerJob;

    @Override
    public void onMessage(Message message) {
           try{
                ObjectMessage objectMessage = (ObjectMessage)message;
                clientToServerJob = (ClientToServerJob)objectMessage.getObject();
                this.setClientToServerJob(clientToServerJob);
              } catch(Exception e) {
                TestToolLogger.writeBackendLog(e1, EnumLogLevel.ERROR, Global.Log.SERVICE_TYPE_GENERAL); 
             }
    }

    public ClientToServerJob getClientToServerJob() {
        return clientToServerJob;
    }

    public void setClientToServerJob(ClientToServerJob clientToServerJob) {
        this.clientToServerJob = clientToServerJob;
    }

}

QueueBrowser:

public class QueueBrowserDemo implements QueueBrowser {

    private ClientToServerJob clientToServerJob;
    private TransportConfiguration transportConfiguration;
    private ConnectionFactory connectionFactory;
    private Connection consumerQueueConnection;
    private Session session;

    private void initSession() throws JMSException {
        transportConfiguration = new TransportConfiguration(NettyConnectorFactory.class.getName());
        connectionFactory = (ConnectionFactory)HornetQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, transportConfiguration);
        consumerQueueConnection = connectionFactory.createConnection();
        session = consumerQueueConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
    }

    @Override
    public Queue getQueue() throws JMSException {
        Queue destination = session.createQueue(GlobalVariable.QUEUE_CLIENT_TO_SERVER_QUEUE);
        return destination;
    }

    @Override
    public String getMessageSelector() throws JMSException {
        Enumeration<?> enumeration = getEnumeration();
        while(enumeration.hasMoreElements()) {
            ObjectMessage objectMessage = (ObjectMessage)enumeration.nextElement();
            clientToServerJob = (ClientToServerJob)objectMessage.getObject();
            this.setClientToServerJob(clientToServerJob);
        }
        close();
        return clientToServerJob.getTableName();
    }

    @Override
    public Enumeration getEnumeration() throws JMSException {
        initSession();
        QueueBrowser browser = session.createBrowser(getQueue());
        Enumeration<?> enumeration = browser.getEnumeration();
        return enumeration;
    }

    @Override
    public void close() throws JMSException {
        session.close();
    }

    public ClientToServerJob getClientToServerJob() {
        return clientToServerJob;
    }

    public void setClientToServerJob(ClientToServerJob clientToServerJob) {
        this.clientToServerJob = clientToServerJob;
    }
}
public class TestClientToServerQueue {

    private ClientToServerQueue clientToServer;
    private ClientToServerJob clientToServerJob;
    private ClientToServerJob clientToServerJobCompare;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    @Before
    public void setUp() throws Exception {
        EnvironmentVariable.initialise();
        // First to initialize hornetQ
        QueueManager.initialise();
        clientToServer = new ClientToServerQueue();
        clientToServerJob = new ClientToServerJob();
        clientToServerJob.setTableName("Test");
        clientToServerJob.setSiteID("RIS-A");
       clientToServer.pushClientToServerJob(clientToServerJob);
    }


    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void TestQueue() {
        QueueBrowserDemo queue = new QueueBrowserDemo();
        try {
            queue.getMessageSelector();
        } catch(JMSException e) {
           fail();
        }
        clientToServerJobCompare = queue.getClientToServerJob();
        Assert.assertEquals(clientToServerJob.getTableName(), clientToServerJobCompare.getTableName());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值