其他一些单元测试技巧

在我以前的文章中,我展示了有关JavaBeans单元测试的一些技巧。 在此博客文章中,我将提供有关单元测试某些相当常见的Java代码的另外两个技巧,即实用程序类Log4J日志记录语句

测试实用程序类

如果您的实用程序类遵循与我倾向于编写的相同的基本设计,则它们由带有私有构造函数和所有静态方法的最终类组成。



实用类测试仪

package it.jdev.example;

import static org.junit.Assert.*;

import java.lang.reflect.*;

import org.junit.Test;

/**
 * Tests that a utility class is final, contains one private constructor, and
 * all methods are static.
 */
public final class UtilityClassTester {

    private UtilityClassTester() {
        super();
    }

    /**
     * Verifies that a utility class is well defined.
     * 
     * @param clazz
     * @throws Exception
     */
    @Test
    public static void test(final Class<?> clazz) throws Exception {
        // Utility classes must be final.
        assertTrue("Class must be final.", Modifier.isFinal(clazz.getModifiers()));

        // Only one constructor is allowed and it has to be private.
        assertTrue("Only one constructor is allowed.", clazz.getDeclaredConstructors().length == 1);
        final Constructor<?> constructor = clazz.getDeclaredConstructor();
        assertFalse("Constructor must be private.", constructor.isAccessible());
        assertTrue("Constructor must be private.", Modifier.isPrivate(constructor.getModifiers()));

        // All methods must be static.
        for (final Method method : clazz.getMethods()) {
            if (!Modifier.isStatic(method.getModifiers()) && method.getDeclaringClass().equals(clazz)) {
                fail("Non-static method found: " + method + ".");
            }
        }
    }

}

该UtilityClassTester本身也遵循上面提到的实用程序类约束,因此有什么更好的方法通过使用它来测试自身来证明其用途:

UtilityClassTester的测试用例

package it.jdev.example;

import org.junit.Test;

public class UtilityClassTesterTest {

    @Test
    public void test() throws Exception {
        UtilityClassTester.test(UtilityClassTester.class);
    }

}

测试Log4J记录事件

调用声明异常的方法时,您将重新声明该异常,或者尝试在try-catch块中对其进行处理。 在后一种情况下,至少要做的是记录捕获的异常。 下面是一个非常简单的示例:

MyService示例

package it.jdev.example;

import java.lang.invoke.MethodHandles;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    private static final Logger LOGGER = Logger.getLogger(MethodHandles.Lookup.class);

    @Autowired
    private MyRepository myRepository;

    public void doSomethingUseful() {
        try {
            myRepository.doSomethingVeryUseful();
        } catch (SomeException e) {
            LOGGER.error("Some very informative error logging.", e);
        }
    }

}

当然,您将需要测试是否正确记录了异常。 遵循以下内容:

MyService日志记录事件的测试用例

package it.jdev.example;

import static org.junit.Assert.*;

import org.apache.log4j.spi.LoggingEvent;
import org.junit.*;
import org.mockito.*;

public class MyServiceTest {

    @Mock
    private MyRepository myRepository;

    @InjectMocks
    private MyService myService = new MyService();

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void thatSomeExceptionIsLogged() throws Exception {
        TestAppender testAppender = new TestAppender();

        Mockito.doThrow(SomeException.class).when(myRepository).doSomethingVeryUseful();
        myService.doSomethingUseful();

        assertTrue(testAppender.getEvents().size() == 1);
        final LoggingEvent loggingEvent = testAppender.getEvents().get(0);
        assertEquals("Some very informative error logging.", loggingEvent.getMessage().toString());
    }

}

但是,如何实现这一目标呢? 事实证明,将新的LogAppender添加到Log4J RootLogger非常容易。

用于Log4J的TestAppender

package it.jdev.example;

import java.util.*;

import org.apache.log4j.*;
import org.apache.log4j.spi.*;

/**
 * Utility for testing Log4j logging events.
 * <p>
 * Usage:<br />
 * <code>
 * TestAppender testAppender = new TestAppender();<br />
 * classUnderTest.methodThatWillLog();<br /><br />
 * LoggingEvent loggingEvent = testAppender.getEvents().get(0);<br /><br />
 * assertEquals()...<br /><br />
 * </code>
 */
public class TestAppender extends AppenderSkeleton {

    private final List<LoggingEvent> events = new ArrayList<LoggingEvent>();

    public TestAppender() {
        this(Level.ERROR);
    }

    public TestAppender(final Level level) {
        super();
        Logger.getRootLogger().addAppender(this);
        this.addFilter(new LogLevelFilter(level));
    }

    @Override
    protected void append(final LoggingEvent event) {
        events.add(event);
    }

    @Override
    public void close() {
    }

    @Override
    public boolean requiresLayout() {
        return false;
    }

    public List<LoggingEvent> getEvents() {
        return events;
    }

    /**
     * Filter that decides whether to accept or deny a logging event based on
     * the logging level.
     */
    protected class LogLevelFilter extends Filter {

        private final Level level;

        public LogLevelFilter(final Level level) {
            super();
            this.level = level;
        }

        @Override
        public int decide(final LoggingEvent event) {
            if (event.getLevel().isGreaterOrEqual(level)) {
                return ACCEPT;
            } else {
                return DENY;
            }
        }

    }

}

翻译自: https://www.javacodegeeks.com/2014/09/some-more-unit-test-tips.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值