chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
0 votes
Try to run this test, I'm expecting that it fails 'cause
m_DBLogger.Log(operationId, detail);

is called 2 times.
If I remove the Try...catch TM fails as expected

using System;
using MbUnit.Framework;
using TypeMock;

[TestFixture]
public class FailSuppressionBug
{
   private ILogger m_logger;
   ILogger m_mockFileLogger = (ILogger) RecorderManager.CreateMockedObject(typeof(ILogger));
   ILogger m_mockDBLogger = (ILogger) RecorderManager.CreateMockedObject(typeof(ILogger));

   [TestFixtureSetUp]
   public void TestFixtureSetUp()
   {
      MockManager.Init();
   }

   [TearDown]
   public void TearDown()
   {
      MockManager.Verify();
   }

   [Test]
   public void DBLoggerShouldBeDisabledUponFirstFailure()
   {
      m_logger = new CompositeLogger(m_mockFileLogger, m_mockDBLogger);

      using (RecordExpectations recorder = RecorderManager.StartRecording())
      {
         m_mockFileLogger.Log("OpenDataFile", "a message to log");
         m_mockDBLogger.Log("OpenDataFile", "a message to log");
         recorder.Throw(new Exception("MOCK: db problem"));

         m_mockFileLogger.Log("OpenDataFile", "another message to log");
      }
      m_logger.Log("OpenDataFile", "a message to log");
      m_logger.Log("OpenDataFile", "another message to log");
   }
}


public interface ILogger
{
   void Log(string operationId, string detail);
}

public class CompositeLogger : ILogger
{
   private ILogger m_fileLogger;
   private ILogger m_DBLogger;


   public CompositeLogger(ILogger fileLogger, ILogger dbLogger)
   {
      m_fileLogger = fileLogger;
      m_DBLogger = dbLogger;
   }

   public void Log(string operationId, string detail)
   {
      m_fileLogger.Log(operationId, detail);
      try
      {
         m_DBLogger.Log(operationId, detail);
      }
      catch (Exception)
      {
         m_DBLogger = new NullLogger();
      }
   }
}

public class NullLogger : ILogger
{
   public void Log(string operationId, string detail)
   {
      //do nothing
   }
}
asked by acarpe (1.7k points)

4 Answers

0 votes
Acarpe hi,

TypeMock does not fail the test with the try..catch because actually the test has 3 logger objects involved and only the first 2 are mocked.

As far as I could tell:
In the first call to m_Logger.Log() the call to : m_DBLogger.Log(operationId, detail); throws an exception (as you specified in the recording phase) causing the m_logger instance to replace his m_DBLogger instance with a new NullLogger which is not mocked.

In the second call to m_Logger.Log() you actually call the log method once on the m_fileLoggerinstance (a recorded call) and once on the m_DBLogger which is now a NUllLogger object (and not mocked) since the NullLogger.log method does nothing the test succeeds.

when you remove the try..catch thing the m_DBLogger instance does not change resulting in the expected behaviour.

You can see the additional calls in the following recording block (you can replace it with your own recording block and still have the test pass)
        using (RecordExpectations recorder = RecorderManager.StartRecording())
        {
            NullLogger nullLogger = new NullLogger();
            m_mockFileLogger.Log("OpenDataFile", "a message to log");
            m_mockDBLogger.Log("OpenDataFile", "a message to log");
            recorder.Throw(new Exception("MOCK: db problem"));
            m_mockFileLogger.Log("OpenDataFile", "another message to log");
            nullLogger.Log("OpenDataFile", "another message to log");
        }


hope this helps,
let me know if you encounter any more problems.
answered by lior (13.2k points)
0 votes
The point is not the NullLogger, suppose this code without that class:
using System;
using MbUnit.Framework;
using TypeMock;

[TestFixture]
public class FailSuppressionBug
{
   private ILogger m_logger;
   ILogger m_mockFileLogger = (ILogger) RecorderManager.CreateMockedObject(typeof(ILogger));
   ILogger m_mockDBLogger = (ILogger) RecorderManager.CreateMockedObject(typeof(ILogger));

   [TestFixtureSetUp]
   public void TestFixtureSetUp()
   {
      MockManager.Init();
   }

   [TearDown]
   public void TearDown()
   {
      MockManager.Verify();
   }

   [Test]
   public void DBLoggerShouldBeDisabledUponFirstFailure()
   {
      m_logger = new CompositeLogger(m_mockFileLogger, m_mockDBLogger);

      using (RecordExpectations recorder = RecorderManager.StartRecording())
      {
         m_mockFileLogger.Log("OpenDataFile", "a message to log");
         m_mockDBLogger.Log("OpenDataFile", "a message to log");
         recorder.Throw(new Exception("MOCK: db problem"));

         m_mockFileLogger.Log("OpenDataFile", "another message to log");
      }
      m_logger.Log("OpenDataFile", "a message to log");
      m_logger.Log("OpenDataFile", "another message to log");
   }
}


internal interface ILogger
{
   void Log(string operationId, string detail);
}

internal class CompositeLogger : ILogger
{
   private ILogger m_fileLogger;
   private ILogger m_DBLogger;


   public CompositeLogger(ILogger fileLogger, ILogger dbLogger)
   {
      m_fileLogger = fileLogger;
      m_DBLogger = dbLogger;
   }

   public void Log(string operationId, string detail)
   {
      m_fileLogger.Log(operationId, detail);
      try
      {
         m_DBLogger.Log(operationId, detail);
      }
      catch (Exception)
      {
         //do nothing
      }
   }
}


This test pass when it should not.
When I write a test at first stage I want to see it to fail.
In this case the test say that dblogger.log should be called only 1 time but in the production code it is called 2 times, and TypeMock say nothing about this.

Only after I seeing the test fail I'll change my test to write the correct behaviour.

I hope I was clear,
thank you,
Antonio.
answered by acarpe (1.7k points)
0 votes
Antonio hi,

I completely missed your point, sorry for that.

Anyway the problem is that by default, expectation checking is done during the call time, when an unexpected method is called (or wrong parameters are passed) a verification execption is thrown imidiately. The verify method at the end of the test just make sure that all expectation were met.

Unfortunately, this exception is caught by catch block you have there, and doesnt fail the test.

You can solve this problem in several ways:
1) Don't catch all exceptions just those which were meant to be thrown.
2) you can also make sure that a method is called a specific amount of times by using MockObject.GetCallCount().

Also MockManager.ValidateArgsOnVerify should have been helpefull in this case since it suppose to postpone the exception throwing to the verify call.however, after trying to use it I think we might have a problem there so I'll inverstigate it some more and if theres an actual bug there we will try solving it please let me know if you need a patch for this.

One last thing, please note that usually if you call a method of a mocked instance which has not been expected the real code will be called. only for inetrafces and abstarct calsses an exception is thrown. this can be changed of course using the "strict" property of MockManager/Mock
answered by lior (13.2k points)
0 votes
Hi
Just to keep you posted, The issue with ValidateArgsOnVerify was solved.
The fix was inclued in our latest version (4.0.2).
answered by lior (13.2k points)
...