对调用静态方法的类进行单元测试 [英] Unit-testing a class that calls a static method

查看:555
本文介绍了对调用静态方法的类进行单元测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试对 A类进行单元测试,该类称为 B类的静态方法。类别 B本质上具有Google番石榴缓存,可从给定键的缓存中检索值(对象),或使用服务适配器将对象加载到缓存中(如果发生缓存丢失)。反过来,service-adapter类还具有其他自动关联的依赖关系来检索对象。

I am trying to unit-test a class 'A' which calls a static method of a class 'B'. Class 'B' essentially has a google guava cache which retrieves a value(Object) from the cache given a key, or loads the object into the cache (in case of a cache-miss) using a service adapter. The service-adapter class in turn has other autowired dependencies to retrieve the object.

这些是出于说明目的的类:

These are the classes for illustration purposes:

A类

public class A {
    public Object getCachedObject(String key) {
        return B.getObjectFromCache(key);
    }
}

B类

public class B {

    private ServiceAdapter serviceAdapter;

    public void setServiceAdapter(ServiceAdapter serAdapt) {
        serviceAdapter = serAdapt;
    } 

    private static final LoadingCache<String, Object> CACHE = CacheBuilder.newBuilder()
                .maximumSize(100) 
                .expireAfterWrite(30, TimeUnit.MINUTES)
                .build(new MyCacheLoader());

    public static Object getObjectFromCache(final String key) throws ExecutionException {
        return CACHE.get(warehouseId);
    }

    private static class MyCacheLoader extends CacheLoader<String, Object>  {

        @Override
        public Object load(final String key) throws Exception {
            return serviceAdapter.getFromService(key)
        }
    }
}

服务适配器类

public class ServiceAdapter {
        @Autowired
        private MainService mainService

        public Object getFromService(String key) {
            return mainService.getTheObject(key);
        }
    }

我能够成功完成集成测试并获取从(或加载)高速缓存中的值。但是,我无法编写A类的单元测试。这是我尝试过的:

I am able to do the integration test successfully and fetch (or load) the value from (or into) the cache. However, I am unable to write the unit-test for class A. This is what I have tried:

A类的单元测试

@RunWith(EasyMocker.class)
public class ATest {
    private final static String key = "abc";
    @TestSubject
    private A classUnderTest = new A();

    @Test
    public void getCachedObject_Success() throws Exception {
        B.setServiceAdapter(new ServiceAdapter());
        Object expectedResponse = createExpectedResponse(); //some private method 
        expect(B.getObjectFromCache(key)).andReturn(expectedResponse).once();
        Object actualResponse = classUnderTest.getCachedObject(key);
        assertEquals(expectedResponse, actualResponse);
    }
}

当我运行单元测试时,它失败并显示ServiceAdapter类上的NullPointerException,将在该类上进行调用:mainService.getTheObject(key)。

When I run the unit-test, it fails with a NullPointerException at ServiceAdapter class where the call: mainService.getTheObject(key) is made.

如何在对A类进行单元测试时模拟ServiceAdapter的依赖关系。我不应该只担心A类具有的直接依赖关系,即。 B.

How do I mock the dependency of ServiceAdapter while unit-testing class A. Shouldn't I be just concerned about the immediate dependency that class A has, viz. B.

我确定自己做的事情根本上是错的。我应该如何为类A编写单元测试?

I am sure I am doing something fundamentally wrong. How should I write the unit-test for class A?

推荐答案

您现在知道了为什么静态方法被认为是单元测试的坏习惯,
,因为它们几乎无法嘲笑,尤其是。

You now know why static method are deemed bad practice for unit testing, as they make mocking almost impossible, esp. if they are stateful.

因此,将B static 方法重构为一组非静态的方法更为实用。

It is hence more practical to refactor B static methods into a set of non-static public ones.

A类应该通过构造函数或setter注入获得B类的实例注入。然后,在您的ATest中,使用类B的模拟实例化类A,并根据测试用例返回您想要的任何内容,并根据此断言来进行声明。

Class A should get an instance of class B injected, either via constructor or setter injection. In Your ATest you then instantiate class A with a mock of class B and have it return whatever you like depending on your test case and base your assertions on that.

因此您真正测试了 unit ,它最终应该是A类的公共接口。(这也是为什么我希望类在理想的世界中只有一个公共方法。)

By doing so you really test the unit, which in the end should be the public interface of class A. (This is also why I like for a class to have only one public method in an ideal world.)

关于您的特定示例:B的模拟也不应该关心它自己的依赖性。您当前在测试中编写:

Regarding to your specific example: The mock of B should also not care about its own dependencies. You currently write in your test:

 B.setServiceAdapter(new ServiceAdapter());       

您在 ATest 中。不在 BTest 中。 ATest 应该仅具有 B mock ,因此传递<$不需要c $ c> ServiceAdapter 。

You are in ATest. Not in BTest. ATest should only have a mock of B, so passing an instance of the ServiceAdapter should not be required.

您只应关心A的公共方法的行为,并且在某些特定的响应下可能会改变B的公共方法。

You only should care how A's public methods behaves, and that may change given certain responses of B's public methods.

我还感到奇怪的是,您要测试的方法基本上只是B的包装。也许这对您来说很有意义,但这也暗示了对我来说,您可能想已经在A中注入了 Object 而不是B的实例。

What I also find odd is that the method you want to test basically only a wrapper to B. Maybe this makes sense in your case yet this also hints to me that you maybe want to already inject an Object in A instead of an instance of B.

如果希望不会在模拟地狱中迷路,这确实有助于每个类拥有尽可能少的公共方法,而这些方法又具有尽可能少的依赖关系。我为每个班级争取三个依存关系,在特殊情况下最多允许五个依存关系。 (每个依赖关系可能会对模拟开销产生巨大影响。)

If you want to not get lost in mocking hell it really helps to have as less public methods per class which in turn have as less dependencies as possible. I strive for at three dependencies per class, and allow up to five on special occasions. (Each dependency may have huge impact on the mocking overhead.)

如果依赖关系过多,则肯定可以将某些部分移至其他/新服务中。

If you have too many dependencies, certainly some parts can be moved to other/new services.

这篇关于对调用静态方法的类进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆