How can I test the behavior of a function in a unit test?

Where a function just calls another function or performs actions. How can I check it? I am currently applying all functions to return a value so that I can assert the return values ​​of the function. However, I think this approach is massaging the API, because in production code. I don't need these functions to return a value. Any good solutions?

I think object layout might be a possible solution. I want to know when should I use assert and when should I use mock objects? Is there a common guideline?

thank

+2


source to share


4 answers


Sorry for not answering directly, but ... are you sure you have an accurate balance when testing?

I wonder too whether you are experiencing ? Do you really need to test a function that just delegates to another?

Returns only for tests

I agree with you, when you write you don't want to add return values ​​that are only useful for tests , not production. This clutters up your API, making it less understandable, which ends up being a huge cost.

Also, your return value may seem correct for the test, but it says nothing about the implementation returning a return value that matches the implementation, so the test probably proves nothing .

Expenses



Note that testing has an initial cost , the cost of writing a test. If implementation is very simple, the risk of failure is ridiculously low, but testing time is still piling up (over a hundred or a thousand cases, this ends up being pretty serious).

But what's more, every time you refactor your production code, you probably have to refactor your tests as well. Thus, the cost of maintaining your tests will be high .

Testing the implementation

Testing what a method does (what other methods it calls, etc.) is critical, just like testing a private method ... There are a few points:

  • it is fragile and expensive : any refactoring of the code will break the tests, therefore it increases the cost of maintenance
  • Testing a private method does not bring more safety to your production code because your production code does not make that call. It looks like something you really don't need.
  • When the code delegates effectively to another, the implementation is so simple that the risk of errors is very low and the code almost never changes, so what works once (when you write it) will never break "> strong> ...
+2


source


Let's use BufferedStream.Flush () as an example method that returns nothing; how would we test this method if we wrote it ourselves?

There is always some observable effect, otherwise the method would not exist. So the answer might be to check for the effect:

[Test]
public void FlushWritesToUnderlyingStream()
{
   var memory = new byte[10];
   var memoryStream = new MemoryStream(memory);
   var buffered = new BufferedStream(memoryStream);

   buffered.Write(0xFF);
   Assert.AreEqual(0x00, memory[0]); // not yet flushed, memory unchanged
   buffered.Flush();
   Assert.AreEqual(0xFF, memory[0]); // now it has changed
}

      



The trick is to structure your code so that these effects aren't too hard to observe in a test:

  • explicitly skip co-author objects, just like passing a memoryStream to a BufferedStream in the constructor. This is called addiction injection .
  • with an interface, just as programmed BufferedStream

    against an interface Stream

    . This allows you to go through simpler, test-friendly implementations (like MemoryStream

    in this case) or use a mocking framework (like MoQ or RhinoMocks ), which is great for unit testing.
+4


source


Yes, mock is generally fine when you want to check that a specific function is being called and that specific parameters are being passed.

Here's how to do it in Typemock (C #):

Isolate.Verify.WasCalledWithAnyArguments(()=> myInstance.WeatherService("","", null,0));
Isolate.Verify.WasCalledWithExactArguments(()=> myInstance. StockQuote("","", null,0));

      

In general, you should make the most of Assert

it until you can get it (for example, when you need to check if you are using the external API of the web service correctly, in which case you can't / don't want to communicate with the web service directly). In this case, you are using mock to check that a specific method of the web service has been called correctly with the correct parameters.

+2


source


"I want to know when should I use assert and when should I use mock objects? Is there a general guideline?"

There is an absolute, fixed and important rule.

Your tests must contain assert. The assert presence is what you use to check if a test passed or failed. A test is a method that calls a "component under test" (function, object, etc.) in a particular fixture and makes specific statements about the behavior of the components.

The test confirms something about the component under test. Every test must have a statement, or it is not a test. If he has no statements, it is not clear what you are doing.

A mock is a component replacement to simplify test configuration. It is a "mock" or "mock" or "fake" component that replaces the real component. You use mocks to replace something and make testing easier.

Let's say you are going to test function a. And function call function b.

Tests for function a must have an assertion (or is it not a test).

Tests for a may need a mock for function b. To isolate the two functions, you test a with a mock for function b.

Tests for function b must have assertion (or is it not a test).

The b tests may not need something mocking. Or perhaps b is calling the OS API. Perhaps it should be a mockery. Or perhaps b is writing to a file. Perhaps it should be a mockery.

+1


source







All Articles