How to keep my unit tests DRY when the mockery does not work?

Edit:

It seems that in trying to provide some solutions to my own problem, I blurred the whole problem. So I'm modifying the question a bit.

Suppose I have this class:

public class ProtocolMessage : IMessage { public IHeader GetProtocolHeader(string name) { // Do some logic here including returning null // and throw exception in some cases return header; } public string GetProtocolHeaderValue(string name) { IHeader header = GetProtocolHeader(name); // Do some logic here including returning null // and throw exception in some cases return value; } } 

It really doesn't matter what happens in these methods. The important thing is that I have several unit tests to cover the GetProtocolHeader method, covering all situations (returning the correct title, zero or exception), and now I am writing unit tests for GetProtocolHeaderValue .

If GetProtocolHeaderValue is dependent on an external dependency, I could mock it and inject it (I use Moq + NUnit). Then my unit test will just check the expectation that the external dependency has been called and the expected value is returned. An external dependency would be tested according to its own unit test, and I would do it, but how to proceed in this example, where the method is not an external dependency?

Clarification of the problem:

I believe that my test package for GetProtocolHeaderValue should check if GetProtocolHeader returns a header, null or exception. Therefore, the main question is whether to write tests where GetProtocolHeader will actually be executed (some tests will be duplicated because they will test the same code as the tests for GetProtocolHeader itself), or should I use the mocking approach described by @adrift and @Eric Nicholson, where I will not run the real GetProtoclHeader , but just configure mock to return a header, null or exception when calling this method?

+6
source share
7 answers

In a call to GetProtocolHeaderValue do you really need to know if it GetProtocolHeader ?

Of course, it’s enough to know that it gets the correct value from the correct header. How this came about is not related to unit test.

You are testing units of functionality, the unit of GetProtocolHeaderValue functionality is whether it returns the expected value, given the name of the header.

It’s true that you can protect against inadequate caching or cross-infection, or get the value from a different header, but I don’t think the testing that it called GetProtocolHeader is the best way to do this, you can conclude that it somehow chose the correct header due to the fact that it returned the expected value for the header.

As long as you create your tests and validate the data so that duplicate headers do not mask errors, then everything should be fine.

EDIT for the updated question:

  • If GetProtocolHeader is fast, reliable and idempotent, I still think there is no need to mock it. The disadvantage of any of these three aspects is (IMO) the main reason for ridicule.

    If (as I suspect in the title of the question), the reason you want to mock it is because the preamble needed to create the appropriate state to return the real value is too verbose, and you would prefer not to repeat its two tests, why Wouldn’t it be done at the setup stage?

  • One of the roles performed by good unit tests is documentation .

    If someone wants to know how to use their class, they can test the tests and possibly copy and modify the test code according to their purpose. This becomes difficult if the real idiom of use has been obscured by the creation and introduction of ridicule.

  • Mocks can hide potential errors.

    Say GetProtocolHeader throws an exception if the name is empty. You create the layout, respectively, and make sure GetProtocolHeaderValue handles this exception accordingly. You later decided that GetProtocolHeader should return null for an empty name. If you forget to update your layout, GetProtocolHeaderValue("") will now behave differently in real life and test cases.

  • Mocking can be an advantage if the layout is less detailed than installation, but first give the points above.

    Although you give three different GetProtocolHeader (header, zero, or exception) that GetProtocolHeaderValue should experience, I believe the first one is likely to be a "range of headers." (for example, what does it do with a header that is present but empty? How does it relate to leading and trailing spaces? What about non-ASCII characters?)? If the setting for all of them is extremely detailed, it would be better to ridicule.

+4
source

I often use a partial layout (in Rhino) or equivalent (e.g. CallsBaseMethod in FakeItEasy) to make fun of the actual class I'm testing. You can then make GetProtocolHeader virtual and make fun of your calls. You can argue that this violates the principle of shared responsibility, but this is still clearly very cohesive code.

Alternatively, you can make a method like

 internal static string GetProtocolHeaderValue(string name, IHeader header ) 

and verify that the processing is performed independently. The public GetProtocolHeaderValue method would not have any / many tests.

Edit: In this particular case, I would also like to add GetValue () as an extension method for IHeader. That would be very easy to read, and you can still do a zero check anyway.

+3
source

I probably missed something, but given the code above, it seems to me that you don't need to worry about whether this is caused or not.

There are two possibilities:

  • In order for the GetProtocolHeader() method to be publicly available, in this case you write a set of tests that will tell you whether it works as expected or not.
  • This is its implementation detail and should not be publicly available, except that you want to test it directly, but in this case all you really need is a set of tests that tell you that GetProtocolHeaderValue() works as needed.

In any case, you are testing open functionality and at the end of the day it all matters. If it was a dependency, then yes, you could worry about whether it was caused, but if it is not its detail and does not matter?

+1
source

With Moq, you can use CallBase to execute an equivalent partial layout in Rhino Mocks.

In your example, change GetProtocolHeader to virtual , then create the ProtocolMessage layout by setting CallBase = true . Then you can configure the GetProtocolHeader method as you wish and use the GetProtocolHeaderValue base class GetProtocolHeaderValue .

For more information, see "Layout Setup" in the quick start of moq.

+1
source

Why not just change GetProtocolHeaderValue (string name) so that it calls 2 methods, the second one accepts IHeader? Thus, you can test the entire // part of the logical part in a separate test using the RetrieveHeaderValue method, without worrying about Mocks. Sort of:

 public string GetProtocolHeaderValue(string name) { IHeader header = GetProtocolHeader(name); return RetrieveHeaderValue(IHeader header); } 

Now you can easily test both parts of GetProtocolHeaderValue. Now you still have the same problems with testing this method, but the amount of logic in it has been minimized.

Following the same line of thinking, these methods can be extracted in the IHeaderParser interface, and GetProtocol methods will be used in IHeaderParser, which would be trivial for testing / mock.

 public string GetProtocolHeaderValue(string name, IHeaderParser parser) { IHeader header = parser.GetProtocolHeader(name); return parser.HeaderValue(IHeader header); } 
+1
source

It's all about oppinion, pure tdd-ists will not say ridicule, mock at this mockery.

There is something wrong with my honest oppinion with the code you wrote, GetProtocolHeader seems important enough to not be discarded as an implementation detail, since you defined it publicly.

The problem here is the second GetProtocolHeaderValue method, which is not able to use an existing instance of IHeader

I would suggest GetValue (string name) in the IHeader interface

+1
source

Try a simple thing that might work.

  • If the actual implementation of GetProtocolHeader () is quick and easy to manage (for example, to simulate headers, exceptions, and exceptions), just use it.
  • If not (i.e. the real implementation takes a lot of time or you can easily simulate 3 cases), then look at redesigning so that the restrictions are reduced.

I refrain from using Mocks if it is absolutely necessary (for example, file / network / external dependency), but, as you know, this is just a personal choice, not a rule. Make sure that the choice is worth the additional cognitive cost (lower readability) of the test.

0
source

Source: https://habr.com/ru/post/902948/


All Articles