GP Coder

Trang chia sẻ kiến thức lập trình Java

  • Java Core
    • Basic Java
    • OOP
    • Exception Handling
    • Multi-Thread
    • Java I/O
    • Networking
    • Reflection
    • Collection
    • Java 8
  • Design pattern
    • Creational Pattern
    • Structuaral Pattern
    • Behavior Pattern
  • Web Service
    • SOAP
    • REST
  • JPA
  • Java library
    • Report
    • Json
    • Unit Test
  • Message Queue
    • ActiveMQ
    • RabbitMQ
  • All
Trang chủ Java library Unit Test JUnit – Parameterized Test

JUnit – Parameterized Test

Đăng vào 08/03/2019 . Được đăng bởi GP Coder . 8214 Lượt xem . Toàn màn hình

Trong bài viết này, chúng ta sẽ cùng tìm hiểu cách tạo và thực hiện các test case với tham số hóa trong Junit (parameterized test).

Nội dung

  • 1 JUnit Parameterized Test là gì?
  • 2 Ví dụ sử dụng Parameterized Test
  • 3 @Theory và @DataPoints là gì?

JUnit Parameterized Test là gì?

Một parameterized test là một phương thức test bình thường, được thực hiện lặp đi lặp lại bằng cách sử dụng các tham số test khác nhau. Nó giúp chúng ta tiết kiệm thời gian trong việc thực hiện nhiều lần cùng một phương thức test với các loại đầu vào khác nhau để kiểm tra các kết quả khác nhau của chức năng.

Chúng ta có thể truyền đối số vào các phương thức unit test thông qua các cách sau:

  • Truyền thông qua đối số của constructor (Constructor Injection).
  • Đánh dấu annotation @Parameter trên các filed (Fields injection).

Ví dụ sử dụng Parameterized Test

Giả sử chúng ta có một lớp cần test như sau:


package com.gpcoder.junit.parameterized;

public class Fibonacci {

	public static int compute(int n) {
		if (n <= 1) {
			return n;
		}
		return compute(n - 1) + compute(n - 2);
	}
}

Sử dụng Parameterized thông qua Constructor

Chúng ta sẽ thực hiện lần lượt các bước sau:

  • Tạo một class test được đánh dấu @RunWith(Parameterized.class).
  • Tạo một public static method được đánh dấu @Parameters và return một Collection của Objects (ví dụ Array) như bộ dữ liệu test.
  • Tạo một public constructor nhận các đối số bằng với số phần tử của một object trong bộ dữ liệu test.
  • Tạo các test case sử dụng các field đã được khởi tạo trong constructor.

package com.gpcoder.junit.parameterized;

import static org.junit.Assert.assertEquals;

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(value = Parameterized.class)
public class ParameterizedContructorTest {

	private int number;
	private int expected;

	// Inject via constructor
	public ParameterizedContructorTest(int number, int expected) {
		this.number = number;
		this.expected = expected;
	}

	// The name attribute is optional, provide an unique name for test
    @Parameters(name = "{index}: Fibonacci({0}) = {1}")
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][]{
        	{ 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
        });
    }

    @Test
    public void test_addTwoNumbes() {
        assertEquals(expected, Fibonacci.compute(number));
    }
}

Kết quả test của chương trình trên:

Sử dụng Parameterized thông qua Fields

Chúng ta sẽ thực hiện lần lượt các bước sau:

  • Tạo một class test được đánh dấu @RunWith(Parameterized.class).
  • Tạo một public static method được đánh dấu @Parameters và return một Collection của Objects (ví dụ Array) như bộ dữ liệu test.
  • Tạo một public field được đánh dấu @Parameter(value = index).
  • Tạo các test case sử dụng các field đã được khai báo với @Parameter.

package com.gpcoder.junit.parameterized;

import static org.junit.Assert.assertEquals;

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.Parameter;
import org.junit.runners.Parameterized.Parameters;

@RunWith(value = Parameterized.class)
public class ParameterizedMultiFieldTest {

	// The index of the parameter is 0
	// Default value is always "value=0"
	@Parameter(value = 0)
	public int number; // NOT private

	// The index of the parameter is 1
	@Parameter(value = 1)
	public int expected; // NOT private

	// The name attribute is optional, provide an unique name for test
	@Parameters(name = "{index}: Fibonacci({0}) = {1}")
	public static Collection<Object[]> data() {
		return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
	}

	@Test
	public void testFibonacci() {
		assertEquals(expected, Fibonacci.compute(number));
	}
}

Chạy test class trên, chúng ta có cùng kết quả với việc sử dụng Parameterized thông qua Constructor.

Trường hợp phương thức test chỉ có 1 field cần inject, chúng ta không cần thuộc tính value trong @Parameter, mặc định value của nó là 0. Ví dụ:


package com.gpcoder.junit.parameterized;

import java.util.Arrays;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;

@RunWith(value = Parameterized.class)
public class ParameterizedSingleFieldTest {

	// Default value is always "value=0"
	@Parameter
	public int number; // NOT private

	// The name attribute is optional, provide an unique name for test
	@Parameters
	public static Iterable<? extends Object> dataset() {
		return Arrays.asList(1, 2);
	}

	@Test
	public void testIsPositiveNumber() {
		Assert.assertEquals(true, number > 0);
	}
}

Kết quả test chương trình trên:

@Theory và @DataPoints là gì?

Ngoài cách sử dụng parameterized test case với @Parameters ở trên, chúng ta có thể sử dụng Annotation @Theory và @DataPoints.

Tương tự như @Parameters, @DataPoints được sử dụng để return một bộ dữ liệu test. Tuy nhiên, nó đơn giản hơn @Parameters, chúng ta không cần phải tạo một constructor hay field tương ứng.

Để thực thi test với các @DataPoints, chúng ta cần phải sử dụng @RunWith(Theories.class). Các phương thức test sử dụng data từ @DataPoints, chúng ta sẽ thay thế @Test trên phương thức bằng @Theory.

Ví dụ 1: chúng ta sẽ sử dụng lại các ví dụ ở trên, với cách dùng @Theory và @DataPoints.


package com.gpcoder.junit.parameterized;

import static org.junit.Assert.assertEquals;

import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

@RunWith(Theories.class)
public class TheoryAndDataPointTest1 {

	@DataPoints
	public static int[][] data() {
		return new int[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } };
	}

	@Theory
	public void testFibonacci(final int[] inputs) {
		System.out.println(String.format("Testing with %d and %d", inputs[0], inputs[1]));
		assertEquals(inputs[1], Fibonacci.compute(inputs[0]));
	}
}

Ví dụ 2: Trường hợp chúng ta muốn tạo từng bộ data riêng riêng lẻ.


package com.gpcoder.junit.parameterized;

import static org.junit.Assert.assertEquals;

import org.junit.experimental.theories.DataPoint;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

@RunWith(Theories.class)
public class TheoryAndDataPointTest2 {

	@DataPoint
	public static int[] input1 = new int[] { 0, 0 };
	
	@DataPoint
	public static int[] input2 = new int[] { 1, 1 };
	
	@DataPoint
	public static int[] input3 = new int[] { 2, 1 };
	
	@DataPoint
	public static int[] input4 = new int[] { 3, 2 };
	
	@DataPoint
	public static int[] input5 = new int[] { 4, 3 };
	
	@DataPoint
	public static int[] input6 = new int[] { 5, 5 };
	
	@DataPoint
	public static int[] input7 = new int[] { 6, 8 };

	@Theory
	public void testFibonacci(final int[] inputs) {
		System.out.println(String.format("Testing with %d and %d", inputs[0], inputs[1]));
		assertEquals(inputs[1], Fibonacci.compute(inputs[0]));
	}
}

Ví dụ 3: Trường hợp có nhiều @DataPoints trong cùng một class test, chúng ta có thể đặt tên cho chúng, và sử dụng @FromDataPoints.


package com.gpcoder.junit.parameterized;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;

import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.FromDataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

@RunWith(Theories.class)
public class TheoryAndDataPointTest3 {

	@DataPoints("data1")
	public static int[][] dataPoints = new int[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } };

	@DataPoints("data2")
	public static int[][] data() {
		return new int[][] { { 0, 999 }};
	}

	@Theory
	public void givenNumber_WhenValidValue_ThenEquals(@FromDataPoints("data1") final int[] inputs) {
		System.out.println(String.format("Testing with %d and %d", inputs[0], inputs[1]));
		assertEquals(inputs[1], Fibonacci.compute(inputs[0]));
	}

	@Theory
	public void givenNumber_WhenInvalidValue_ThenNotEquals(@FromDataPoints("data2") final int[] inputs) {
		System.out.println(String.format("Testing with %d and %d", inputs[0], inputs[1]));
		assertNotEquals(inputs[1], Fibonacci.compute(inputs[0]));
	}
}

Ví dụ 4: Trường hợp chúng ta muốn test một bộ dữ liệu 1 với từng dữ liệu trong bộ dữ liệu 2, chúng ta có thể sử dụng @TestedOn


package com.gpcoder.junit.parameterized;

import static org.junit.Assert.assertTrue;

import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.experimental.theories.suppliers.TestedOn;
import org.junit.runner.RunWith;

class Checker {
	public static boolean isGreaterThan(int number1, int number2) {
		return number1 > number2;
	}
}

@RunWith(Theories.class)
public class TheoryAndDataPointTest4 {

	@Theory
	public void testAllNumber1GreaterThanAllNumber2(@TestedOn(ints = { 3, 4, 5 }) int number1,
			@TestedOn(ints = { 1, 2 }) int number2) {
		System.out.println(String.format("Testing with %d and %d", number1, number2));
		assertTrue(Checker.isGreaterThan(number1, number2));
	}
}

Output của test trên:


Testing with 3 and 1
Testing with 3 and 2
Testing with 4 and 1
Testing with 4 and 2
Testing with 5 and 1
Testing with 5 and 2

Ví dụ 5: Chúng ta sẽ tạo một custom Parameter Supplier tương tự như @TestedOn


package com.gpcoder.junit.parameterized;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import org.junit.experimental.theories.ParametersSuppliedBy;

@Retention(RetentionPolicy.RUNTIME)
@ParametersSuppliedBy(BetweenSupplier.class)
public @interface Between {
	int first();

	int last();
}


package com.gpcoder.junit.parameterized;

import java.util.ArrayList;
import java.util.List;

import org.junit.experimental.theories.ParameterSignature;
import org.junit.experimental.theories.ParameterSupplier;
import org.junit.experimental.theories.PotentialAssignment;

public class BetweenSupplier extends ParameterSupplier {
	@Override
	public List<PotentialAssignment> getValueSources(ParameterSignature sig) throws Throwable {
		Between annotation = sig.getAnnotation(Between.class);

		List<PotentialAssignment> list = new ArrayList<>();
		for (int i = annotation.first(); i <= annotation.last(); i++) {
			list.add(PotentialAssignment.forValue(null, i));
		}
		return list;
	}
}


package com.gpcoder.junit.parameterized;

import static org.junit.Assert.assertTrue;

import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

class Checker2 {
	public static boolean isGreaterThan(int number1, int number2) {
		return number1 > number2;
	}
}

@RunWith(Theories.class)
public class TheoryAndDataPointTest5 {

	@Theory
	public void multiplyIsInverseOfDivideWithInlineDataPoints(@Between(first = 3, last = 5) int number1,
			@Between(first = 1, last = 2) int number2) {
		System.out.println(String.format("Testing with %d and %d", number1, number2));
		assertTrue(Checker2.isGreaterThan(number1, number2));
	}
}

Thực thi class trên, chúng ta có cùng kết quả như @TestedOn.

Tài liệu tham khảo:

  • https://github.com/junit-team/junit4/wiki/Parameterized-tests
4.8
06
Nếu bạn thấy hay thì hãy chia sẻ bài viết cho mọi người nhé! Và Donate tác giả

Shares

Chuyên mục: Unit Test Được gắn thẻ: JUnit

Làm thế nào để thực thi một nhóm các class test trong JUnit?
Đơn giản hóa Unit Test với JUnit Rule

Có thể bạn muốn xem:

  • Mockito – Annotations (28/03/2019)
  • Một số Annotation cơ bản của JUnit (05/03/2019)
  • Tổng hợp các bài viết về Unit Test trong Java (15/04/2019)
  • Đơn giản hóa Unit Test với JUnit Rule (11/03/2019)
  • Làm thế nào để chạy lại một failed Test trong JUnit? (21/03/2019)

Bình luận

bình luận

Tìm kiếm

Bài viết mới

  • Clean code 13/01/2024
  • Giới thiệu CloudAMQP – Một RabbitMQ server trên Cloud 02/10/2020
  • Kết nối RabbitMQ sử dụng Web STOMP Plugin 19/06/2020
  • Sử dụng publisher confirm trong RabbitMQ 16/06/2020
  • Sử dụng Dead Letter Exchange trong RabbitMQ 13/06/2020

Xem nhiều

  • Hướng dẫn Java Design Pattern – Factory Method (97939 lượt xem)
  • Hướng dẫn Java Design Pattern – Singleton (97603 lượt xem)
  • Giới thiệu Design Patterns (87600 lượt xem)
  • Lập trình đa luồng trong Java (Java Multi-threading) (86256 lượt xem)
  • Giới thiệu về Stream API trong Java 8 (83695 lượt xem)

Nội dung bài viết

  • 1 JUnit Parameterized Test là gì?
  • 2 Ví dụ sử dụng Parameterized Test
  • 3 @Theory và @DataPoints là gì?

Lưu trữ

Thẻ đánh dấu

Annotation Authentication Basic Java Behavior Pattern Collection Creational Design Pattern Cấu trúc điều khiển Database Dependency Injection Design pattern Eclipse Exception Executor Service Google Guice Gson Hibernate How to Interceptor IO Jackson Java 8 Java Core JDBC JDK Jersey JMS JPA json JUnit JWT Message Queue Mockito Multithreading OOP PowerMockito RabbitMQ Reflection Report REST SOAP Structuaral Pattern Swagger Thread Pool Unit Test Webservice

Liên kết

  • Clean Code
  • JavaTpoint
  • Refactoring Guru
  • Source Making
  • TutorialsPoint
  • W3Schools Online Web Tutorials

Giới thiệu

GP Coder là trang web cá nhân, được thành lập với mục đích lưu trữ, chia sẽ kiến thức đã học và làm việc của tôi. Các bài viết trên trang này chủ yếu về ngôn ngữ Java và các công nghệ có liên quan đến Java như: Spring, JSF, Web Services, Unit Test, Hibernate, SQL, ...
Hi vọng góp được chút ít công sức cho sự phát triển cộng đồng Coder Việt.

Donate tác giả

Tìm kiếm các bài viết của GP Coder với Google Search

Liên hệ

Các bạn có thể liên hệ với tôi thông qua:
  • Trang liên hệ
  • Linkedin: gpcoder
  • Email: contact@gpcoder.com
  • Skype: ptgiang56it

Follow me

Copyright 2025 © GP Coder · All Rights Reserved · Giới thiệu · Chính sách · Điều khoản · Liên hệ ·

Share

Blogger
Delicious
Digg
Email
Facebook
Facebook messenger
Flipboard
Google
Hacker News
Line
LinkedIn
Mastodon
Mix
Odnoklassniki
PDF
Pinterest
Pocket
Print
Reddit
Renren
Short link
SMS
Skype
Telegram
Tumblr
Twitter
VKontakte
wechat
Weibo
WhatsApp
X
Xing
Yahoo! Mail

Copy short link

Copy link