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 – Custom Hamcrest Matchers

JUnit – Custom Hamcrest Matchers

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

Trong bài trước chúng ta đã cùng tìm hiểu về thư viện Hamcrest Matchers và cách sử dụng Matcher có sẵn của Hamcrest với JUnit. Trong bài này chúng ta sẽ cùng tìm hiểu cách tự viết một Matcher.

Để tạo một Custom Matcher chúng ta có thể sử dụng một trong các cách sau:

  • Extend từ một abstract class FeatureMatcher.
  • Extend từ một abstract class TypeSafeMatcher.
  • Extend từ một abstract class TypeSafeDiagnosingMatcher.
  • Extend từ một abstract class BaseMatcher.

Nội dung

  • 1 Custom Matcher sử dụng FeatureMatcher
  • 2 Custom Matcher sử dụng TypeSafeMatcher
  • 3 Custom Matcher sử dụng TypeSafeDiagnosingMatcher
  • 4 Custom Matcher sử dụng BaseMatcher

Custom Matcher sử dụng FeatureMatcher

Chúng ta cần tạo một Custom Matcher khi cần wrap các Matcher đã tồn tại.

Một FeatureMatcher cần:

  • Một constructor với các tham số sau:
    • Matcher<?> subMatcher : là một matcher cần được wrap.
    • String featureDescription : là một mô tả về feature test.
    • String featureName : là một mô tả khi test bị failed.
  • Override phương thức featureValueOf() : trả về giá trị sẽ được truyền vào phương thức được wrap matches()/ matchesSafely().
  • Một static method để gọi khởi tạo matcher. Phương thức này nên được đánh dấu annotation @Factory để cho các tool/ IDE đây là một Hamcrest static factory method.

Ví dụ: chúng ta sẽ tạo Matcher để so sánh độ dài của mộ string.


package com.gpcoder.junit.hamcrest.custom;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

import org.hamcrest.Factory;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.junit.Test;

class GetLength extends FeatureMatcher<String, Integer> {

	private GetLength(Matcher<? super Integer> subMatcher, String featureDescription, String featureName) {
		super(subMatcher, featureDescription, featureName);
	}

	@Factory
	public static GetLength length(Matcher<? super Integer> subMatcher) {
		return new GetLength(subMatcher, "a string of length that", "length");
	}

	@Override
	protected Integer featureValueOf(String actual) {
		return actual.length();
	}
	
}
public class FeatureMatcherExample {

	@Test
	public void givenString_WhenLengthIs7_ThenCorrect() {
	    assertThat("gpcoder", GetLength.length(is(7)));
	}

	@Test
	public void givenString_WhenLengthIs8_ThenIncorrect() {
	    assertThat("gpcoder", GetLength.length(is(8)));
	}
}

Kết quả test:

Custom Matcher sử dụng TypeSafeMatcher

Chúng ta có thể tạo một Custom Matcher bằng cách tạo một class extend từ một abstract class TypeSafeMatcher. Nó được gọi là safe bởi vì giá trị được truyền vào phương thức matchesSafely() đã được check null và cast đúng kiểu dữ liệu trước khi phương thức này được gọi.

Một TypeSafeMatcher cần implement 2 phương thức:

  • describeTo() : được sử dụng để tạo mô tả về những gì Matcher đang kiểm tra. Mô tả này là những gì được Hamcrest sử dụng sau phần “Expected:” output của nó cho một Matcher không thành công.
  • matchesSafely() : là phương thức được Hamcrest thực thi khi chúng ta muốn sử dụng Matcher của mình để kiểm tra giá trị.

Ví dụ: chúng ta sẽ tạo Matcher để kiểm tra một số có phải là một số dương hay không.


package com.gpcoder.junit.hamcrest.custom;

import static org.junit.Assert.assertThat;

import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Test;

class IsPositiveInteger extends TypeSafeMatcher<Integer> {

	@Factory
	public static Matcher<Integer> isAPositiveInteger() {
		return new IsPositiveInteger();
	}

	@Override
	public void describeTo(Description description) {
		description.appendText("a positive integer");
	}

	@Override
	protected boolean matchesSafely(Integer integer) {
		return integer > 0;
	}
}

public class TypeSafeMatcherExample {

	@Test
	public void givenInteger_WhenAPositiveValue_ThenCorrect() {
		int num = 1;
		assertThat(num, IsPositiveInteger.isAPositiveInteger());
	}

	@Test
	public void givenInteger_WhenANegativeValue_ThenIncorrect() {
		int num = -1;
		assertThat(num, IsPositiveInteger.isAPositiveInteger());
	}
}

Kết quả test:

Custom Matcher sử dụng TypeSafeDiagnosingMatcher

Tương tự như TypeSafeMatcher, chúng ta cũng có thể tạo một custom Matcher bằng cách extend từ abstract class TypeSafeDiagnosingMatcher. Tuy nhiên, TypeSafeDiagnosingMatcher cho phép chúng ta có thể mô tả chi tiết hơn về lý do tại sao Matcher không khớp. Mô tả này là những gì được Hamcrest sử dụng sau phần “but:” output của nó cho một Matcher không thành công.

Ví dụ: chúng ta sẽ tạo Matcher để kiểm tra một số có phải là một chẵn hay không.


package com.gpcoder.junit.hamcrest.custom;

import static org.junit.Assert.assertThat;

import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import org.junit.Test;

class IsEven extends TypeSafeDiagnosingMatcher<Integer> {

	@Factory
	public static IsEven isEven() {
		return new IsEven();
	}

	@Override
	protected boolean matchesSafely(Integer integer, Description description) {
		description.appendText("was ").appendValue(integer).appendText(", which is an Odd number");
		return integer % 2 == 0;
	}

	@Override
	public void describeTo(Description description) {
		description.appendText("An Even number");
	}
}

public class TypeSafeDiagnosingMatcherExample {

	@Test
	public void givenInteger_WhenEvenValue_ThenCorrect() throws Exception {
		assertThat(4, IsEven.isEven());
	}

	@Test
	public void givenInteger_WhenOddValue_ThenIncorrect() throws Exception {
		assertThat(5, IsEven.isEven());
	}
}

Kết quả test:

Custom Matcher sử dụng BaseMatcher

Tất cả những Matcher trên đều extend từ một abstract class BaseMatcher<T>. Tất nhiên, chúng ta cũng có thể tạo một custom Matcher bằng cách extend từ BaseMatcher<T>.

Ví dụ: chúng ta sẽ tạo một MatchCombiner để thực thi một chain matcher với nhau.


package com.gpcoder.junit.hamcrest.custom;

import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;

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

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.Test;

class MatcherCombinator<T> extends BaseMatcher<T> {
    private final List<Matcher<? super T>> matchers = new ArrayList<>();
    private final List<Matcher<? super T>> failedMatchers = new ArrayList<>();

    private MatcherCombinator(final Matcher<? super T> matcher) {
        matchers.add(matcher);
    }

    public MatcherCombinator<T> and(final Matcher<? super T> matcher) {
        matchers.add(matcher);
        return this;
    }

    @Override
    public boolean matches(final Object item) {
        boolean matchesAllMatchers = true;
        for (final Matcher<? super T> matcher : matchers) {
            if (!matcher.matches(item)) {
                failedMatchers.add(matcher);
                matchesAllMatchers = false;
            }
        }
        return matchesAllMatchers;
    }

    @Override
    public void describeTo(final Description description) {
        description.appendValueList("\n", " " + "and" + "\n", "", matchers);
    }

    @Override
    public void describeMismatch(final Object item, final Description description) {
        description.appendText("\n");
        for (Iterator<Matcher<? super T>> iterator = failedMatchers.iterator(); iterator.hasNext();) {
            final Matcher<? super T> matcher = iterator.next();
            description.appendText("Expected: <"); description.appendDescriptionOf(matcher).appendText(" but "); matcher.describeMismatch(item, description); if (iterator.hasNext()) { description.appendText(">\n");
            }
        }
    }

    public static <LHS> MatcherCombinator<LHS> matches(final Matcher<? super LHS> matcher) {
        return new MatcherCombinator<LHS>(matcher);
    }
}

public class MatcherCombinatorExample {
	
	@Test
	public void givenList_WhenEmptyList_ThenInCorrect() {
	    List<Integer> list = new ArrayList<>();
	    assertThat(list, MatcherCombinator.matches(hasSize(1)).and(contains(3)));
	}
	
	@Test
	public void givenList_WhenNotEmptyList_ThenInCorrect() {
	    List<Integer> list = new ArrayList<>();
	    list.add(3);
	    assertThat(list, MatcherCombinator.matches(hasSize(1)).and(contains(3)));
	}
}

Kết quả test:

Tài liệu tham khảo:

  • https://www.javacodegeeks.com/2015/11/custom-hamcrest-matchers.html
  • https://www.vogella.com/tutorials/Hamcrest/article.html
5.0
03
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ẻ: Hamcrest, JUnit

JUnit – Hamcrest Matchers
Làm thế nào để chạy lại một failed Test trong JUnit?

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

  • Làm thế nào để thực thi một nhóm các class test trong JUnit? (07/03/2019)
  • PowerMockito – Suppressing Unwanted Behavior (10/04/2019)
  • Làm thế nào để Test Jersey Rest API với JUnit? (22/08/2019)
  • Mockito – Control mock’s behavior (01/04/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 (97309 lượt xem)
  • Hướng dẫn Java Design Pattern – Singleton (96950 lượt xem)
  • Giới thiệu Design Patterns (86585 lượt xem)
  • Lập trình đa luồng trong Java (Java Multi-threading) (85401 lượt xem)
  • Giới thiệu về Stream API trong Java 8 (82970 lượt xem)

Nội dung bài viết

  • 1 Custom Matcher sử dụng FeatureMatcher
  • 2 Custom Matcher sử dụng TypeSafeMatcher
  • 3 Custom Matcher sử dụng TypeSafeDiagnosingMatcher
  • 4 Custom Matcher sử dụng BaseMatcher

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