什么时候适合在单元测试中绕过封装? [英] When is it appropriate to bypass encapsulation in unit tests?

查看:76
本文介绍了什么时候适合在单元测试中绕过封装?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近意识到 powermock的白盒功能. (总而言之,它允许您直接测试私有方法或直接从单元测试中修改私有成员,同时保持私有性!)

我知道在单元测试中除了可见方法以外的其他东西都有些想法,但是令人讨厌,有时您只需要一个简单的测试来确保深层次的辅助方法能够完成应有的工作. ..无需花费巨大的开销来准备父方法过滤到您的方法所需的参数和模拟..然后,您必须做些魔术才能提取该内部方法的结果.简而言之,有时测试那些内部方法需要最复杂且最难遵循的单元测试. (而且,仅供参考,我们公司的政策是100%的单元测试覆盖率.这些方法必须经过测试!)

我知道一种方法更改方法访问器以便进行单元测试(例如,从private更改为protected,但是powermock Whitebox允许直接测试而不更改源代码.

妈妈仍然说:只是因为你可以,并不意味着你应该."

测试这样的内部方法是否合适?如果是这样,我应该使用什么经验法则?

解决方案

让我们开始了解public方法,因为它们构成了我们的程序:如果我们知道所有公共API都可以工作,那么我们就知道该程序可以工作.如果您的应用程序结构良好,您将拥有几个API层,这些层会不断地将测试精简为更小的单元,因此您可以清楚地了解到哪里可能出问题了.

关于private方法要实现的事情是,它们总是会在某个时刻被public方法调用,该方法将得到测试. private方法是被测单元的实现细节,因此不应单独进行测试.

此辅助方法不受API约束,因此无法保证其效果将保持不变;现在,在不调整整体功能的情况下更改实现细节将使您无法通过该方法进行测试.因此,即使您的程序仍然可以正常运行,您现在的测试也很糟糕.这些测试是易变的:不会改变功能的重构仍然会导致您不得不删除单元测试,因为它们不再相关(这很危险,因为您必须非常确定测试实际上是冗余).

为什么要测试private方法?

只有几件事是黑白的,这不是其中之一.我相信新代码不应测试private方法,但这就是我与众不同的地方:新代码.

如果您继承旧版代码库,则该体系结构可能不会很漂亮.您可能需要跳过障碍,整体代码流可能会更好.

因此,您要做的第一件事是编写一些单元测试,这些单元测试可以保证在您中断功能时告诉您.到目前为止,到目前为止,我们可以开始实际的重构过程了.让我们从这种500行的private方法开始吗?

请牢记前面的说明,现在您必须仔细检查代码流,看看使用什么private方法的public方法,并在进行更改时留意它们.

为了易于使用,我将在此处编写测试,以仅将private方法的合同与您的应用程序的其余部分进行测试:只要方法的结果没有不同,您就可以知道它不会影响任何其他旧方法.有了这些知识,您现在可以轻松地重构该方法中的所有内容.当某事确实破裂时;您可以立即使用您的辅助方法,而不必使用公共API方法.

请记住,我只会对大型重构操作执行此操作:这些首先是临时测试,而实现细节"实际上是500行代码:这是一个细节,它本身包含许多细节

我应该如何测试private方法?

关于如何使这些可测试性,您有一些选择:

反射

我相信这就是WhiteBox所使用的.但是,它引起了一些注意:没有静态类型(更改方法名意味着破坏测试),它很昂贵并且使用反射的代码往往更难阅读.

PrivateObject

我还没有研究确切的实现,但是我很确定这在幕后使用了反射.基本上,它通过方便的界面提供反射方法.我还没有看到它的用处(部分原因是我将反射作为绝对的最后手段),但是它就在那里.

MSDN

提取

这将是我的第一种方法:这种辅助方法是否足够重要,足以使其独立存在?您可能会对此表示是",因为显然它已经足够重要,因此您需要对其进行显式测试.

公共

最明显的选择:只需将其设为public.这遵循与提取相同的思想:它可以独立存在吗?".此处的区别在于,您仍然将其视为当前类的一部分,您只是提升了它的访问权限,而将其提取到另一个类中也表明您在谈论的不仅是辅助方法.

内部

此选项采用中间路线:如果我将其设置为内部(C#上下文)并使用[InternalsVisibleTo]属性,则可以将其设置为internal,同时使测试程序集可以对其进行测试(并且仍然保留它)来自公共API).

这带来了这样的风险,即人们只能将其视为实现细节,并在破坏测试(说明特定实现)的同时更改实现.

此外,这还增加了您的应用程序与测试之间的耦合.


所有人都认为这取决于您自己的偏好:有多种方法可以测试private方法,并且双方都有一些参数.就我个人而言,我将避免测试此类实现细节的麻烦,而会看到可以做些什么来改进我的体系结构.问题很可能会这样解决.

I've recently become aware of powermock's Whitebox functionality. (In summary, it allows you to directly test private methods or modify private members directly from unit tests--while keeping them private!)

I know that there are some frames of thought that frown upon unit testing anything other than visible methods, but darn it, sometimes you just want a simple test to ensure a deep level helper method is doing what it is supposed to do...without going through what could be huge overhead to prepare the parameters and mocks needed for the parent methods to filter down to your method..and then you have to just about do magic to extract the results of that inner method. In short, sometime testing those inner methods requires the most complex and hard to follow unit tests. (And, fyi, our company has a policy of 100% unit test coverage. Those methods must be tested!)

I'm aware that one methodology is to change method accessors in order to allow for unit tests (e.g., change from private to protected, but powermock Whitebox allows a direct test without changing the source code.

Still, Mamma always said, "Just because you can, doesn't mean you should."

Is it ever appropriate to test inner methods like this? If so, what is the rule of thumb I should use?

解决方案

Let's start off by acknowledging that this is a partially religious debate.

It is my point of view that testing private methods is something you should often avoid, but there are exceptions.

Why shouldn't I test private methods?

Because you're testing whether or not your application works, not necessarily how it works. A private method is not part of the API that your program exposes and it is essentially an implementation detail.

A common argument against working around the limitations set upon you by encapsulation and still testing it, is that it might break your tests that test for that specific implementation.

In "normal" testing, we test the public methods because they form our program: if we know all public APIs work then we know the program will work. If your application is nicely structured, you will have several API layers which constantly refine the tests into smaller units so you have a clear overview where something might go wrong somewhere down the line.

The thing to realize about private methods is that they will always get called at some point by a public method, which will get tested. A private method is an implementation detail of your unit under test and as such should not be tested separately.

This helper method is not bound by the API and thus there is no guarantee that it effects will remain the same; changing an implementation detail without adjusting the overall functionality will now break your test on that method. So even though your program still works perfectly fine, you now have a broken test. These tests are volatile: a refactor that doesn't change your functionality can still cause you to have to remove unit tests because they are no longer relevant (which is dangerous in itself because you'll have to be very sure the test actually is redundant).

Why should I test private methods?

Only a few things are black and white and this isn't one of them. I believe new code should not test private methods but that's where I make the distinction: new code.

If you inherit a legacy codebase, chances are the architecture isn't very pretty. You might have to jump through hoops and the overall codeflow can probably be better.

So the first thing you do is write some unit tests that are guaranteed to tell you when you break functionality. So far so good, we can now get to the actual refactoring process. Let's start with this 500-lines private method?

Keeping the previous remarks in mind, you would now have to look through the codeflow and see what public methods use that private method and keep an eye on them as you make your changes.

For the sake of ease-of-use, I would write tests here that test just the contract of that private method against the rest of your application: as long as your result from the method doesn't differ, you know that it won't affect any of the other legacy methods. With this knowledge you can now comfortably refactor everything inside that method. When something does break; you can immediately work with your helper method instead of having to go through the public API methods.

Keep in mind that I would only do this for big refactoring operations: these are temporary tests in the first place and the "implementation detail" is in reality 500 lines of code: that's a detail with a lot of details on its own.

How should I test private methods?

You have a few options here on how to make these testable:

Reflection

I believe this is what WhiteBox uses. It raises some eyebrows though: there is no static typing (changing the methodname means breaking the test), it's expensive and code using reflection tends to be harder to read.

PrivateObject

I haven't looked at the exact implementation but I'm pretty sure this uses reflection behind the scenes. Basically it provides the reflection approach through a convenient interface. I haven't seen it used much (partly because I would use reflection as an absolute last resort) but it's there.

MSDN

Extraction

This would be my first approach: is this helper method important enough to make it stand on its own? Chances are you will say "yes" to this since apparently it is already important enough that you want an explicit test for it.

Public

The most obvious choice: simply make it public. This follows the same idea as Extraction: "can it stand on its own?". The difference here is that you still consider this as a part of the current class, you just elevate its access whereas extracting it to a different class also indicates that you're talking about more than just a helper method.

Internal

This option takes the middle route: if I make it internal (C# context) and use the [InternalsVisibleTo] attribute, you can make it internal while giving your test assembly the possibility to test it (and still keeping it away from the public API).

This brings the risk that people only see it as an implementation detail and change the implementation while breaking the tests (that accounted for a specific implementation).

Furthermore this also increases coupling between your application and the tests.


All considered it is up to your own preference: there are several options to test private methods and there are some arguments for both sides. Personally I would stay away from the hassle of testing such implementation details and instead see what I can do to improve my architecture. Chances are the problem will solve itself that way.

这篇关于什么时候适合在单元测试中绕过封装?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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