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