ChatGPT解决这个技术问题 Extra ChatGPT

使用 Mockito 模拟静态方法

我编写了一个工厂来生产 java.sql.Connection 对象:

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {

    @Override public Connection getConnection() {
        try {
            return DriverManager.getConnection(...);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

我想验证传递给 DriverManager.getConnection 的参数,但我不知道如何模拟静态方法。我将 JUnit 4 和 Mockito 用于我的测试用例。有没有一种模拟/验证这个特定用例的好方法?

你不能通过设计来使用mockito :)
@MariuszS Mockito(或 EasyMock 或 jMock)不支持模拟 static 方法并非设计使然,而是偶然。这种限制(以及不支持模拟 final 类/方法或 new-ed 对象)是用于实现模拟的方法的自然(但非预期)结果,其中动态创建实现/扩展的新类要模拟的类型;其他模拟库使用其他方法来避免这些限制。这也发生在 .NET 世界中。
@Rogério 感谢您的解释。 github.com/mockito/mockito/wiki/FAQ 我可以模拟静态方法吗? 不能。 Mockito 更喜欢面向对象和依赖注入,而不是难以理解的静态过程代码。改变。 这个限制背后也有一些设计 :)
@MariuszS 我读到这是试图驳回合法用例而不是承认该工具具有无法(轻松)消除的限制,并且没有提供任何合理的理由。顺便说一句,here is such a discussion 表示相反的观点,带有参考。
自 v3.4.0 起,Mockito 现在支持模拟静态方法github.com/mockito/mockito/pull/1955

M
MariuszS

在 Mockito 上使用 PowerMockito

示例代码:

@RunWith(PowerMockRunner.class)
@PrepareForTest(DriverManager.class)
public class Mocker {

    @Test
    public void shouldVerifyParameters() throws Exception {

        //given
        PowerMockito.mockStatic(DriverManager.class);
        BDDMockito.given(DriverManager.getConnection(...)).willReturn(...);

        //when
        sut.execute(); // System Under Test (sut)

        //then
        PowerMockito.verifyStatic();
        DriverManager.getConnection(...);

    }

更多信息:

为什么 Mockito 不模拟静态方法?


虽然这在理论上可行,但 having a hard time in practice...
不幸的是,这样做的巨大缺点是需要 PowerMockRunner。
sut.execute() ?方法?
System Under Test,需要对 DriverManager 进行 mock 的类。 kaczanowscy.pl/tomek/2011-01/testing-basics-sut-and-docs
仅供参考,如果您已经在使用 JUnit4,则可以执行 @RunWith(PowerMockRunner.class) 及低于该 @PowerMockRunnerDelegate(JUnit4.class)
J
JohnK

从 Mockito 3.4.0 开始可以在 Mockito 中模拟静态方法。有关更多详细信息,请参阅:

https://github.com/mockito/mockito/releases/tag/v3.4.0

https://github.com/mockito/mockito/issues/1013

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#static_mocks

assertEquals("foo", Foo.method());
try (MockedStatic mocked = mockStatic(Foo.class)) {
 mocked.when(Foo::method).thenReturn("bar");
 assertEquals("bar", Foo.method());
 mocked.verify(Foo::method);
}
assertEquals("foo", Foo.method());

在你的情况下,是这样的:

  @Test
  public void testStaticMockWithVerification() throws SQLException {
    try (MockedStatic<DriverManager> dummy = Mockito.mockStatic(DriverManager.class)) {
      DatabaseConnectionFactory factory = new MySQLDatabaseConnectionFactory();
      dummy.when(() -> DriverManager.getConnection("arg1", "arg2", "arg3"))
        .thenReturn(new Connection() {/*...*/});

      factory.getConnection();

      dummy.verify(() -> DriverManager.getConnection(eq("arg1"), eq("arg2"), eq("arg3")));
    }
  }

注意:模拟静态方法需要 mockito-inline 依赖而不是 mockito-core。

对于 JUnit5,还要添加:

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-junit-jupiter</artifactId>
  <version>${mockito.version}</version>
  <scope>test</scope>
</dependency>

对我来说,一个 Testclass 提供了一些非常好的见解,如何使用新的 statickMock-Feature:StaticMockTest.java,另请参阅版本 3.4.23.4.6 中的错误修正
无法解析“Mockito”中的方法“mockStatic”
它现在通过删除 'org.mockito:mockito-all:2.0.2-beta' 依赖项来工作。以前,我使用了 mockito 的多个依赖项,例如(core、inline、all)。
感谢 leokom 指出这一点。但是,我不喜欢 Mockito 提出的 try-with-resources 方法,因此将其替换为 JUnit5 扩展。我将添加一个答案来描述它。它允许您简单地在测试类上为静态模拟创建一个带注释的字段。更干净,尤其是当您需要模拟多个静态时。
c
codebox

避开您无法避免使用的静态方法的典型策略是创建包装对象并改用包装对象。

包装器对象成为真正的静态类的外观,您无需对其进行测试。

包装器对象可能类似于

public class Slf4jMdcWrapper {
    public static final Slf4jMdcWrapper SINGLETON = new Slf4jMdcWrapper();

    public String myApisToTheSaticMethodsInSlf4jMdcStaticUtilityClass() {
        return MDC.getWhateverIWant();
    }
}

最后,您的被测类可以使用这个单例对象,例如,拥有一个用于现实生活的默认构造函数:

public class SomeClassUnderTest {
    final Slf4jMdcWrapper myMockableObject;

    /** constructor used by CDI or whatever real life use case */
    public myClassUnderTestContructor() {
        this.myMockableObject = Slf4jMdcWrapper.SINGLETON;
    }

    /** constructor used in tests*/
    myClassUnderTestContructor(Slf4jMdcWrapper myMock) {
        this.myMockableObject = myMock;
    }
}

在这里,您有一个可以轻松测试的类,因为您不直接使用具有静态方法的类。

如果您正在使用 CDI 并且可以使用 @Inject 注释,那么它会更容易。只需让您的 Wrapper bean @ApplicationScoped,将该东西作为协作者注入(您甚至不需要凌乱的构造函数来进行测试),然后继续进行模拟。


我创建了一个工具来自动生成包含静态调用的 Java 8“mixin”接口:github.com/aro-tech/interface-it 生成的 mixin 可以像任何其他接口一样被模拟,或者如果您的测试类“实现”了该接口,您可以覆盖它的任何方法在测试的子类中。
6
6324

我有一个类似的问题。接受的答案对我不起作用,直到我做出更改:@PrepareForTest(TheClassThatContainsStaticMethod.class),根据 PowerMock's documentation for mockStatic

而且我不必使用 BDDMockito

我的课:

public class SmokeRouteBuilder {
    public static String smokeMessageId() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("Exception occurred while fetching localhost address", e);
            return UUID.randomUUID().toString();
        }
    }
}

我的测试课:

@RunWith(PowerMockRunner.class)
@PrepareForTest(SmokeRouteBuilder.class)
public class SmokeRouteBuilderTest {
    @Test
    public void testSmokeMessageId_exception() throws UnknownHostException {
        UUID id = UUID.randomUUID();

        mockStatic(InetAddress.class);
        mockStatic(UUID.class);
        when(InetAddress.getLocalHost()).thenThrow(UnknownHostException.class);
        when(UUID.randomUUID()).thenReturn(id);

        assertEquals(id.toString(), SmokeRouteBuilder.smokeMessageId());
    }
}

无法弄清楚 ?.mockStatic 和 ?.when 目前使用 JUnit 4
PowerMock.mockStatic & Mockito.when 似乎不起作用。
对于以后看到这个的任何人,对我来说,我必须输入 PowerMockito.mockStatic(StaticClass.class);
您需要包含 powermock-api-mockito maven arterfact。
C
ChrisM

如前所述,您不能使用 mockito 模拟静态方法。

如果更改测试框架不是一种选择,您可以执行以下操作:

为 DriverManager 创建一个接口,模拟这个接口,通过某种依赖注入将其注入并在该模拟上进行验证。


你好,你能举个例子吗?谢谢你。
G
Gayan Weerakutti

对于使用 JUnit 5 的用户,Powermock 不是一个选项。您需要以下依赖项才能使用 Mockito 成功模拟静态方法。

testCompile    group: 'org.mockito', name: 'mockito-core',           version: '3.6.0'
testCompile    group: 'org.mockito', name: 'mockito-junit-jupiter',  version: '3.6.0'
testCompile    group: 'org.mockito', name: 'mockito-inline',         version: '3.6.0'

mockito-junit-jupiter 添加对 JUnit 5 的支持。

mockito-inline 依赖项提供了对模拟静态方法的支持。

例子:

@Test
void returnUtilTest() {
    assertEquals("foo", UtilClass.staticMethod("foo"));

    try (MockedStatic<UtilClass> classMock = mockStatic(UtilClass.class)) {

        classMock.when(() -> UtilClass.staticMethod("foo")).thenReturn("bar");

        assertEquals("bar", UtilClass.staticMethod("foo"));
     }

     assertEquals("foo", UtilClass.staticMethod("foo"));
}

try-with-resource 块用于使静态模拟保持临时状态,因此仅在该范围内模拟。

不使用 try 块时,请确保在完成断言后关闭模拟。

MockedStatic<UtilClass> classMock = mockStatic(UtilClass.class)
classMock.when(() -> UtilClass.staticMethod("foo")).thenReturn("bar");
assertEquals("bar", UtilClass.staticMethod("foo"));
classMock.close();

模拟 void 方法:

在类上调用 mockStatic 时,该类中的所有静态 void 方法都会自动模拟为 doNothing()


s
some random guy

观察:在静态实体中调用静态方法时,需要更改@PrepareForTest 中的类。

例如:

securityAlgo = MessageDigest.getInstance(SECURITY_ALGORITHM);

对于上面的代码,如果您需要模拟 MessageDigest 类,请使用

@PrepareForTest(MessageDigest.class)

如果您有以下内容:

public class CustomObjectRule {

    object = DatatypeConverter.printHexBinary(MessageDigest.getInstance(SECURITY_ALGORITHM)
             .digest(message.getBytes(ENCODING)));

}

然后,您需要准备此代码所在的类。

@PrepareForTest(CustomObjectRule.class)

然后模拟该方法:

PowerMockito.mockStatic(MessageDigest.class);
PowerMockito.when(MessageDigest.getInstance(Mockito.anyString()))
      .thenThrow(new RuntimeException());

我正用头撞墙,试图弄清楚为什么我的静态课程没有嘲笑。您可能会认为,在互联网上的所有教程中,ONE 会涉及的不仅仅是简单的用例。
i
iirekm

我还编写了 Mockito 和 AspectJ 的组合:https://github.com/iirekm/varia/tree/develop/ajmock

您的示例变为:

when(() -> DriverManager.getConnection(...)).thenReturn(...);

提供的链接已失效。
F
Fermin Silva

您可以通过一些重构来做到这一点:

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {

    @Override public Connection getConnection() {
        try {
            return _getConnection(...some params...);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //method to forward parameters, enabling mocking, extension, etc
    Connection _getConnection(...some params...) throws SQLException {
        return DriverManager.getConnection(...some params...);
    }
}

然后您可以扩展您的类 MySQLDatabaseConnectionFactory 以返回模拟连接、对参数进行断言等。

扩展类可以驻留在测试用例中,如果它位于同一个包中(我鼓励你这样做)

public class MockedConnectionFactory extends MySQLDatabaseConnectionFactory {

    Connection _getConnection(...some params...) throws SQLException {
        if (some param != something) throw new InvalidParameterException();

        //consider mocking some methods with when(yourMock.something()).thenReturn(value)
        return Mockito.mock(Connection.class);
    }
}

D
David Miguel

Mockito 无法捕获静态方法,但从 Mockito 2.14.0 开始,您可以通过创建静态方法的调用实例来模拟它。

示例(从 their tests 中提取):

public class StaticMockingExperimentTest extends TestBase {

    Foo mock = Mockito.mock(Foo.class);
    MockHandler handler = Mockito.mockingDetails(mock).getMockHandler();
    Method staticMethod;
    InvocationFactory.RealMethodBehavior realMethod = new InvocationFactory.RealMethodBehavior() {
        @Override
        public Object call() throws Throwable {
            return null;
        }
    };

    @Before
    public void before() throws Throwable {
        staticMethod = Foo.class.getDeclaredMethod("staticMethod", String.class);
    }

    @Test
    public void verify_static_method() throws Throwable {
        //register staticMethod call on mock
        Invocation invocation = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "some arg");
        handler.handle(invocation);

        //verify staticMethod on mock
        //Mockito cannot capture static methods so we will simulate this scenario in 3 steps:
        //1. Call standard 'verify' method. Internally, it will add verificationMode to the thread local state.
        //  Effectively, we indicate to Mockito that right now we are about to verify a method call on this mock.
        verify(mock);
        //2. Create the invocation instance using the new public API
        //  Mockito cannot capture static methods but we can create an invocation instance of that static invocation
        Invocation verification = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "some arg");
        //3. Make Mockito handle the static method invocation
        //  Mockito will find verification mode in thread local state and will try verify the invocation
        handler.handle(verification);

        //verify zero times, method with different argument
        verify(mock, times(0));
        Invocation differentArg = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "different arg");
        handler.handle(differentArg);
    }

    @Test
    public void stubbing_static_method() throws Throwable {
        //register staticMethod call on mock
        Invocation invocation = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "foo");
        handler.handle(invocation);

        //register stubbing
        when(null).thenReturn("hey");

        //validate stubbed return value
        assertEquals("hey", handler.handle(invocation));
        assertEquals("hey", handler.handle(invocation));

        //default null value is returned if invoked with different argument
        Invocation differentArg = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "different arg");
        assertEquals(null, handler.handle(differentArg));
    }

    static class Foo {

        private final String arg;

        public Foo(String arg) {
            this.arg = arg;
        }

        public static String staticMethod(String arg) {
            return "";
        }

        @Override
        public String toString() {
            return "foo:" + arg;
        }
    }
}

他们的目标不是直接支持静态模拟,而是改进其公共 API,以便其他库(如 Powermockito)不必依赖内部 API 或直接复制一些 Mockito 代码。 (source)

免责声明:Mockito 团队认为通往地狱的道路是用静态方法铺就的。但是,Mockito 的工作不是保护您的代码免受静态方法的影响。如果您不喜欢您的团队进行静态模拟,请停止在您的组织中使用 Powermockito。 Mockito 需要发展为一个工具包,对应该如何编写 Java 测试有一个固执的愿景(例如,不要模拟静态!!!)。然而,Mockito 并不是教条主义的。我们不想阻止不推荐的用例,例如静态模拟。这不是我们的工作。


L
Line

要模拟静态方法,您应该使用 Powermock 查看:https://github.com/powermock/powermock/wiki/MockStatic。 Mockito doesn't provide 这个功能。

您可以阅读一篇关于 mockito 的精彩文章:http://refcardz.dzone.com/refcardz/mockito


请不要链接到网站。答案应包括实际可用的答案。如果站点出现故障或更改,则答案不再有效。
D
Dupinder Singh

我在 Mockito 中找到了一种解决方案。此功能仅来自 3.4.0 的版本

https://asolntsev.github.io/en/2020/07/11/mockito-static-methods/

在您的 build.gradle 中,将 mockito-core:3.3.3 替换为 mockito-inline:3.4.0: testImplementation('org.mockito:mockito-inline:3.4.0')

我们要模拟什么类 Buddy { static String name() { return "John"; } }

模拟静态方法 @Test void lookMomICanMockStaticMethods() { assertThat(Buddy.name()).isEqualTo("John");尝试 (MockedStatic theMock = Mockito.mockStatic(Buddy.class)) { theMock.when(Buddy::name).thenReturn("Rafael"); assertThat(Buddy.name()).isEqualTo("Rafael"); } assertThat(Buddy.name()).isEqualTo("John"); }

我认为这可以帮助我们。


我不明白在这里嘲笑有什么用。如果巴迪,它不会抓住。名称()已更改。
B
Benas

由于该方法是静态的,因此它已经拥有您使用它所需的一切,因此它违背了模拟的目的。模拟静态方法被认为是一种不好的做法。

如果您尝试这样做,则意味着您执行测试的方式有问题。

当然,您可以使用 PowerMockito 或任何其他能够做到这一点的框架,但请尝试重新考虑您的方法。

例如:尝试模拟/提供该静态方法使用的对象。


R
RCvaram

当您尝试模拟静态方法时,您必须在 try 块内编写测试。因为重要的是要注意作用域模拟必须由激活模拟的实体关闭。

      try (MockedStatic<Tester> tester = Mockito.mockStatic(Tester.class)) {
            tester.when(() -> Tester.testStatic("Testing..")).thenReturn(mock(ReturnObject.class));
    //Here you have to write the test cases
      }

在上面的例子中,我们必须模拟 Tester 类 testStatic 方法,输入参数为“Testing...”。在这里,该方法将返回一个 ReturnObject 类类型的对象。因此,我们在像上面那样链接时编写 mockito。

不要忘记在 Gradle/maven 中添加以下依赖项

    testImplementation 'org.mockito:mockito-inline:4.3.1'

Z
Zlatan

使用 JMockit 框架。它对我有用。您不必为模拟 DBConenction.getConnection() 方法编写语句。只需下面的代码就足够了。

@Mock 下面是 mockit.Mock 包

Connection jdbcConnection = Mockito.mock(Connection.class);

MockUp<DBConnection> mockUp = new MockUp<DBConnection>() {

            DBConnection singleton = new DBConnection();

            @Mock
            public DBConnection getInstance() { 
                return singleton;
            }

            @Mock
            public Connection getConnection() {
                return jdbcConnection;
            }
         };

S
Shrikant Prabhu

使用 java FunctionalInterface 有一个简单的解决方案,然后将该接口添加为您尝试进行单元测试的类的依赖项。


A
Alexander Stohr

对于模拟静态函数,我可以这样做:

在一些帮助类/对象中创建一个包装函数。 (使用名称变体可能有利于保持事物的分离和可维护性。)

在你的代码中使用这个包装器。 (是的,代码需要在考虑到测试的情况下实现。)

模拟包装函数。

包装器代码片段(不是真正的功能,仅用于说明)

class myWrapperClass ...
    def myWrapperFunction (...) {
        return theOriginalFunction (...)
    }

当然,在单个包装类中累积多个此类函数可能有利于代码重用。


D
Dharman

在这里,我根据我对 leokom 解决方案的回答中承诺的扩展来分享我的 mockito MockStatic 解决方案。

那么,为什么 Mockito 选择 try-with-resources 呢?好吧,仅仅是因为他们想保持一艘整洁的船。毕竟这是一个很好的编程。 Try-with-resources 允许在保证调用 close 方法的情况下进行构造。但是在 JUnit 中,我们已经在 BeforeEach 和 AfterEach 中拥有了它。并且可以使用实现 BeforeEachCallback 和 AfterEachCallback 的扩展轻松地将这些用于通用目的添加到每个测试类。

理论就这么多。让我们为

Instant.now()

我从一个注释开始,以便能够在我的测试类中标记我想用作静态模拟的字段。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface StaticMock {

}

这允许我在我的测试类中创建一个用于静态模拟的字段,我可以在我的扩展类中轻松找到它。

  @StaticMock
  private MockedStatic<Instant> staticInstantMock;

我将我创建的扩展添加到我的测试类中。你有两个选择。

为此目的创建一个 Extension 并将其添加到您还需要的 MockitoExtension 旁边的类中。创建一个扩展并让它从 MockitoExtension 继承。现在你可以在你的测试类上替换 MockitoExtension。

我使用了两者中的后者。

@ExtendWith({CompanyMockitoExtension.class})
class MyExtendedTestClass {

现在我们需要在调用静态时为它返回一些东西:

  @Mock
  private Instant now;

  staticInstantMock.when(Instant::now).thenReturn(now);

整个测试类:

@ExtendWith({CompanyMockitoExtension.class})
class MyExtendedTestClass {

  @StaticMock
  private MockedStatic<Instant> staticInstantMock;

  @Mock
  private Instant now;

  @Test
  void myTestMethod() {
    staticInstantMock.when(Instant::now).thenReturn(now);

    assertThat(Instant::now).isSameAs(now); // This would normally happen in the class you are testing...
  }
}

现在让我们看一下 Extension 类。

import static org.mockito.Mockito.mockStatic;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

public class CompanyMockitoExtension extends MockitoExtension {

  @Override
  public void beforeEach(ExtensionContext context) {
    super.beforeEach(context); // Don't forget to call the super!!
    if (context.getTestInstance().isEmpty()) { // Just to be sure...
      return;
    }
    // Get the unit test instance
    Object testSubject = context.getTestInstance().get();
    initializeStaticMocks(testSubject);
  }

  private void initializeStaticMocks(Object testSubject) {
    // Find all fields that I want to static mock
    List<Field> staticMockFields = ReflectionHelper.getFieldsWithAnnotation(testSubject, StaticMock.class);
    staticMockFields.forEach(field -> initializeStaticMock(field, testSubject));
  }

  private void initializeStaticMock(Field field, Object testSubject) {
    // Get the type of the static mock. It is within the generic MockedStatic<> class type.
    Class<?> typeForStaticMock = (Class<?>) ReflectionHelper.getTypesForGeneric(field)[0];
    try {
      // Now set the field with the mockStatic method of Mockito.
      field.setAccessible(true);
      field.set(testSubject, mockStatic(typeForStaticMock));
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Failed to instantiate Static Mock with type: " + typeForStaticMock.getName());
    }
  }

  @Override
  public void afterEach(ExtensionContext context) {
    super.afterEach(context); // Again, do not forget to call the super.
    if (context.getTestInstance().isEmpty()) {
      return;
    }
    Object testSubject = context.getTestInstance().get();
    closeStaticMocks(testSubject); // Close all static mocks.
  }

  private void closeStaticMocks(Object testSubject) {
    // Again find all fields we annotated
    List<Field> staticMockFields = ReflectionHelper.getFieldsWithAnnotation(testSubject, StaticMock.class);
    staticMockFields.forEach(field -> closeStaticMock(field, testSubject));
  }

  private void closeStaticMock(Field field, Object testSubject) {
    // Get the instance and simply call close.
    MockedStatic<?> mockedStaticInstance = ReflectionHelper.getFieldInstance(field, testSubject, MockedStatic.class);
    mockedStaticInstance.close();
  }
}

这个扩展的好处是你可以添加额外的模拟内容。我在 AfterEach 中的所有模拟上添加了不再交互的验证。现在,当我们使用此扩展程序时,这是自动的。我还为构造模拟添加了与静态模拟类似的行为。

如您所见,我创建了自己的反射助手类。我知道有一些标准的反射助手类,这些可能会更好。这是我的目的。

public class ReflectionHelper {

  public static List<Field> getFieldsWithAnnotation(
      Object testSubject,
      Class<? extends Annotation> annotationType
  ) {
    Class<?> testSubjectClass = testSubject.getClass();

    return Arrays.stream(testSubjectClass.getDeclaredFields())
                 .filter(field -> field.isAnnotationPresent(annotationType))
                 .collect(toUnmodifiableList());
  }

  public static List<Field> getCollectionFields(Object testSubject) {
    Class<?> testSubjectClass = testSubject.getClass();

    return Arrays.stream(testSubjectClass.getDeclaredFields())
                 .filter(field -> Collection.class.isAssignableFrom(field.getType()))
                 .collect(toUnmodifiableList());
  }

  @SuppressWarnings("unchecked")
  public static <T> T getFieldInstance(Field field, Object testSubject, Class<T> type) {
    return (T) getFieldInstance(field, testSubject);
  }

  public static Object getFieldInstance(Field field, Object testSubject) {
    try {
      boolean isStatic = isStatic(field.getModifiers());
      Object context = isStatic ? null : testSubject;
      field.setAccessible(true);
      return field.get(context);
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Failed to get instance of field.");
    }
  }

  public static Type[] getTypesForGeneric(Field field) {
    ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
    return parameterizedType.getActualTypeArguments();
  }
}