天天看點

其他一些單元測試技巧

在我以前的文章中,我展示了有關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