代码之家  ›  专栏  ›  技术社区  ›  lannyf

如果在线程中调用mockito mock静态函数,则该函数不起作用

  •  0
  • lannyf  · 技术社区  · 5 年前

    android应用程序,java类需要根据NotificationManager的状态执行某些操作。

    class Util {
        
            static void setupByPermission(@NonNull final Context appContext) {
                Thread t = new Thread(new Runnable() {
                    public void run() {
                        try {
                            NotificationManagerCompat nm = NotificationManagerCompat.from(appContext);  // should got from stub
                            
                            boolean overallPermission = currentNotificationsPermission(nm);
                            if (overallPermission) {
                                doWithPermission();
                            } else {
                                doWithoutPermission();
                            }
                            
                        } catch (Throwable ex) {}
                    }
                });
                t.start();
            }
    
        static boolean currentNotificationsPermission(@NonNull NotificationManagerCompat nm) {
    
            System.out.println("+++ enter currentNotificationsPermission("+nm+")");
    
            boolean overallPermission = nm.areNotificationsEnabled();// should got result from stub
    
            System.out.println("+++ ========= in currentNotificationsPermission("+nm+"), nm.areNotificationsEnabled() ==> "+overallPermission);
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                if (overallPermission) {
                    List<NotificationChannel> channels = nm.getNotificationChannels();
                    boolean someChannelEnabled = channels.isEmpty();
                    for (NotificationChannel channel : channels) {
                        if (channel.getImportance() != NotificationManagerCompat.IMPORTANCE_NONE) {
                            someChannelEnabled = true;
                            break;
                        }
                    }
                    overallPermission = overallPermission && someChannelEnabled;
                }
            }
    
            System.out.println("+++ --- exit =========== currentNotificationsPermission(), overallPermission:"+overallPermission);
    
            return overallPermission;
        }
        
    }
    

    我想把这个 NotificationManagerCompat.areNotificationsEnabled() 通过返回真或假来强制测试。

    使用mockito inline 3.8.0进行测试

    @Test
    public void test () throws Exception  {
    
       try (MockedStatic<NotificationManagerCompat> nmMoc = Mockito.mockStatic(NotificationManagerCompat.class);
            MockedStatic<Util> utilMoc = Mockito.mockStatic(Util.class)
            ) {
    
            NotificationManagerCompat nmSpy = spy(NotificationManagerCompat.from(application));
            
    when(nmSpy.areNotificationsEnabled())
                .thenReturn(false);  //or true
    
            nmMoc.when(() -> NotificationManagerCompat.from(any(Context.class)))
                .thenReturn(nmSpy);
    
            // test
            final CountDownLatch latch = new CountDownLatch(1);
            utilMoc.setupByPermission(application);
            latch.await(2, TimeUnit.SECONDS);
    
            Mockito.verify(......);
            }
    }
    

    但是存根在线程中时不会被调用。如果存根是相同的 currentNotificationsPermission() .

    让静态函数在线程中工作的存根很热吗?

    0 回复  |  直到 5 年前
        1
  •  1
  •   tgdavies    5 年前

    你的测试似乎有一些问题:

    • 不要嘲笑静态方法。重构代码以使用接口和实现。这也会使依赖注入变得更容易。
    • 阅读Javadoc获取更多信息 MockedStatic 。它明确地告诉您,模拟只在原始线程上工作。
    • 你的代码是如何编译的? utilMoc 是一个 MockedStatic<Util> 那你怎么打电话 setupByPermission
    • 既然你嘲笑了我 Util ,调用 Util 无论如何都不会调用“real”方法,除非您告诉它。请参见下面的示例。
    • 单元测试多线程代码很困难。有一个组件负责执行 Runnable .在测试中,使用运行 可运行的 在当前线程上。
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.MockedStatic;
    import org.mockito.Mockito;
    import org.mockito.junit.jupiter.MockitoExtension;
    
    import static org.assertj.core.api.Assertions.assertThat;
    import static org.mockito.Mockito.when;
    
    @ExtendWith(MockitoExtension.class)
    public class TestExample {
        public static class Foo {
            public static String bar() {
                return "bar";
            }
        }
        @Test
        public void aTest() {
            try (MockedStatic<Foo> foo = Mockito.mockStatic(Foo.class)) {
    // without this line Foo.bar() will return null
                when(Foo.bar()).thenCallRealMethod();
                assertThat(Foo.bar()).isEqualTo("bar");
            }
        }
    }
    

    尽管人们已经投入了大量的努力 可能的 要模拟静态方法,正如您所看到的,这仍然是不简单的。重构需要避免它 直截了当,还有其他好处。