Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper unit testing: testing a method with no [accessible] state or return value

Tags:

unit-testing

I'm somewhat new to unit testing. One thing (for now) about proper testing confuses me.

For instance, how do you test a main method if it has no state and only console output? Like this, where myServer methods & state are private?

 public static void main(String[] args)
 {
     Server myServer = new Server()
     if(myServer.start())
          System.out.println("started");
     else
          System.out.println("failed"); 
 }

I don't want to have to change my code and expose my Server methods & state to make them public.

Note, I'm not asking how to test myServer.start(), I'm asking how to test main() itself.

Please let me know.

Thanks guys, jbu

like image 427
jbu Avatar asked Jan 17 '09 02:01

jbu


People also ask

Which of the following is correct about a unit test case?

Q 10 - Which of the following is correct about a Unit Test Case? A - A Unit Test Case is a part of code which ensures that the another part of code (method) works as expected.

What do you have to avoid in tests in unit testing?

Avoid Test Interdependence You, therefore, cannot count on the test suite or the class that you're testing to maintain state in between tests. But that won't always make itself obvious to you. If you have two tests, for instance, the test runner may happen to execute them in the same order each time.


1 Answers

Main methods are supposed to be very thin and just get the ball rolling. Hence generally not tested. e.g. a .net Winforms app Main method may contain

static void Main()
{
  Application.EnableVisualStyles();
  Application.SetCompatibleTextRenderingDefault(false);
  Application.Run(new MainWindow());
}

If your main method is doing a lot of stuff, consider extracting a method (to a new class if need be). e.g. as shown below... (don't have a JavaIDE handy.. the foll .net code should be easy to translate)

static void Main()
{
  new MainApp().Run();
}

Now if Run can be tested.. Main too is covered. Lets look at MainApp. Refactored the 2 tasks of starting the server and logging the result as 2 methods.

public class MainApp
{
  private Server m_Server;
  public MainApp():this(new Server())
  {}
  public MainApp(Server s)
  {  m_Server = s;      }

  public void Run()
  {  Log(LaunchServer());  }

  private string LaunchServer()
  {  return (m_Server.start() ? "started" : "failed");        }

  protected virtual void Log(string sMessage)
  {  System.Console.WriteLine(sMessage);        }
}

Now lets look at the test code... I'll use the "subclass and override" trick to cache the result as shown below. (You could also use Mocks.. as per taste)

public class FakeApp : MainApp
{
  private string sLastLoggedMessage;

  public FakeApp(Server s) : base(s) { }

  public string getLastLoggedMessage()
  {  return sLastLoggedMessage;        }

  protected override void Log(string sMessage)
  {  sLastLoggedMessage = sMessage;        }
}

the test is now trivial

[TestFixture]
public class TestMainApp
{
  [Test]
  public void TestRun()
  {
    Server s = new Server();
    FakeApp f = new FakeApp(s);

    f.Run();

    Assert.AreEqual("started", f.getLastLoggedMessage());
  }
}

if you can't force Server.start() to succeed or fail as per your desire.. You can create a FakeServer.. subclass and override start() to return fixed values.

If a method does something, it can be tested. If it does not, it should cease to exist. Refactor it away. HTH

like image 149
Gishu Avatar answered Oct 11 '22 21:10

Gishu