The constructor uses the layout of the object, how can I test the method in isolation?

I have a class that looks like this:

class MyClass { private IDependency dep; public MyClass(IDependency dep) { this.dep = dep; this.dep.Reset(); } public void Clear() { this.dep.Reset(); } } 

How to check if the Reset method is correctly called in the Clear method, ignoring what the constructor does?

My Moq test is as follows:

 MockRepository mocks = new MockRepository(MockBehavior.Default); var dep = mocks.Create<IDependency>(); dep.Setup(s => s.Reset()); MyClass myclass = new MyClass(dep.Object); myclass.Clear(): state.Verify(s => s.Reset(), Times.Exactly(1)); 

It fails because Reset was called twice (once in the constructor and once in the Clear method).

+4
source share
5 answers

I was hoping there would be a better way to do this, but the layout would record all calls to Reset , so using a standard Verify call will always return 2. The following is a separate counter that is not very elegant. If there is a built-in way to do this with Moq, I would love to know.

 int clearResetCount = 0; Mock<IDependency> dep = new Mock<IDependency>(); MyClass myclass = new MyClass(dep.Object); dep.Setup(s => s.Reset()).Callback(() => clearResetCount++); Assert.AreEqual(0, clearResetCount, "Expected clear reset count - before."); myclass.Clear(); Assert.AreEqual(1, clearResetCount, "Expected clear reset count - after."); 
+2
source

As suggested by others, you can overturn your own layout or you can set a series of expectations regarding dependency.

For example, you can verify that your method has been called:

 var mock = new Mock<IDependency>(); var subject = new MyClass(mock.Object); subject.Clear(); mock.Verify( dep => dep.Reset(), Times.AtMost(2)); 

However, it is worth noting that working inside the constructor is a known smell of code , and this smell is exacerbated when trying to write tests.

The fact that your constructor needs to call this method for a dependency suggests that this object knows too much information about the details of the implementation of the dependencies. This violates the principle of open closing and closes you from scripts in which you do not want the Reset method to be called when it is initialized.

Also note that for any class or test that uses a specific MyClass object as a dummy parameter, you will need Mock initialization or you will get a NullReferenceException. This adds significant overhead to writing your tests and adds a level of fragility that equates to long-term maintenance and false negatives in your tests. The only way around this is to make everything an interface, which, although effective, is also not the best long-term strategy.

According to http://googletesting.blogspot.com/2009/07/separation-anxiety.html using Factory will reduce some of this communication and open you up for better reuse of this object.

+2
source

I ran into the same problem.

Do the following to make mock only record the behavior of the Clear method:

 MockRepository mocks = new MockRepository(MockBehavior.Default); var dep = mocks.Create<IDependency>(); MyClass myclass = new MyClass(dep.Object); // setting up the mock just before calling the method under test // will ignore any prior call to IDependency.Reset int resetTimes = 0; dep.Setup(s => s.Reset()).Callback(() => resetTimes++); myclass.Clear(); mocks.VerifyAll(); Assert.That(resetTimes, Is.EqualTo(1)); 
+1
source

Instead of using a mock object, you can write a spy. It takes a bit more coding, but the test is easier to read.

 class DependencySpy : IDependency { public int ResetCallCount { get; private set; } public void Reset() { ResetCallCount++; } public void ClearResetCallCount() { ResetCallCount = 0; } } 

The test can be written as

 // Setup var spy = new DependencySpy; MyClass myclass = new MyClass(spy); spy.ClearResetCallCount(); // Exercise myclass.Clear(); // Validate Assert.AreEqual(1, spy.ResetCallCount); 
0
source

You can use reflection to customize the private field to your mocked object. Then just call the Clear method and test the dependency call.

-1
source

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


All Articles