简体   繁体   中英

unit test for method that use other methods

I'm new to unit test and confusing how write unit test method like this:

public Boolean BeepInTime(Interfaces.IDateTime time,TimeSpan beepTime)
{
    Interfaces.IBeep beep= new Beep();
    var h = time.GetTime();
    if (h == beepTime)
    {
       return beep.Beeping();
    }
    else
    {
       return false;
    }
}

public Boolean Beeping()
{
    try
    {
      SystemSounds.Beep.Play();
      return true;
    }
    catch
    {
      return false;
    }
}

When testing BeepInTime , I'd like ( beep.Beeping() ) not running. I read about stub and think in this case I should use stub but it is confusing about how to do this. Could you send some source with simple example about stub.

The solution is - dependency injection.

Each time you do or see ... = new ...(); (construction) think of dependency injection, basically explicit instantiation introduces dependencies and then sometimes writing unit tests for such a code becomes not trivial.

So instead of explicit instantiation of Beep class just inject it. What is good - you already have an interface which abstracts this class - IBeep , this would allow you creatign and injecting a mock instead of a real class instance when writing unit tests.

public Boolean BeepInTime(Interfaces.IBeep beeper, TimeSpan beepTime) 
{
    var h = time.GetTime();            
    if (h == beepTime)            
    {                 
        return beep.Beeping();            
    } 

    return false;
}

Ans just imagine how this technique is helpful in case of injecting DB access service or web-service, you do not need a real DB or web-service while writing unit tests - you just mock DB/web service and that's it.

Example using RhinoMocks:

var mock = MockRepository.GenerateMock<IBeep>();

// setup Beeping() return value == true
mock.Expect(m => m.Beeping()).Return(true);
var isBeepInTime = BeepInTime(mock, timeSpan)

There are lots of different ways to resolve this. The simpliest way is to pass an interface to Beep into the class (either through constructor or method itself).

public Boolean BeepInTime(Interfaces.IDateTime time,TimeSpan beepTime, Interfaces.IBeep beep) 
{ 
    var h = time.GetTime(); 
    if (h == beepTime) 
    { 
        return beep.Beeping(); 
    } 
    else 
    { 
        return false; 
    } 
   } 

In your unit test you create a mock IBeep object and pass in the mock. There are lots of third party mocking frameworks out there also so you do not have to create your own mocks. I personally use Moq and it is a good one. RhinoMocks is also popular. Both are free.

If you do not want to have to create the Beep class in your production code then you could do this...

public Boolean BeepInTime(Interfaces.IDateTime time,TimeSpan beepTime, Interfaces.IBeep beep = null) 
{ 
    if (beep == null)
         beep = new Beep();

    var h = time.GetTime(); 
    if (h == beepTime) 
    { 
        return beep.Beeping(); 
    } 
    else 
    { 
        return false; 
    } 
   } 

NOTE: I would not normally pass the interface into the constructor and assign it to a field rather then pass it into the method, especially if the class is called from many methods). There are many other ways to do this including using NInject. I am just providing these examples to give you a sneak peek into how it is done.

I would recommend doing a few days of research on the internet looking into these things before deciding on any one method.

You first need to remove the dependency to Beep from the BeepInTime method like this:

public Boolean BeepInTime(Interfaces.IDateTime time,TimeSpan beepTime, Interfaces.IBeep beep)
       {           
           var h = time.GetTime();
           if (h == beepTime)
           {

               return beep.Beeping();
           }
           else
           {
               return false;
           }

       }

Then you can Mock out the IBeep impementation when testing using something like Moq:

//Arrange
var mockBeep = new Mock<Interfaces.IBeep>();
mockBeep.Setup(b => b.Beeping())
.Returns(true);

// Act

var result = myClass.BeepInTime(myTime, myBeepTime, mockBeep.Object);

...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM