7

I have written around 20 methods in Java and all of them call some web services. None of these web services are available yet. To carry on with the server side coding, I hard-coded the results that the web-service is expected to give.

Can we unit test these methods? As far as I know, unit testing is mocking the input values and see how the program responds. Are mocking both input and ouput values meaningful?

Edit :

The answers here suggest I should be writing unit test cases.

Now, how can I write it without modifying the existing code ?

Consider the following sample code (hypothetical code) :

    public int getAge()
    {
            Service s = locate("ageservice"); // line 1
            int age = s.execute(empId); // line 2
             return age; // line 3

 }

Now How do we mock the output ?

Right now , I am commenting out 'line 1' and replacing line 2 with int age= 50. Is this right ? Can anyone point me to the right way of doing it ?

0

7 Answers 7

12

Yes.

Use mocks and stubs to simulate the web services' expected behavior. Validate that your code functions correctly under all expected boundary values and equivalence classes.

EDIT:

No, you should not be manually editing the unit test with int age= 50. You should be using dependency injection so that you can easily replace the Service with a mock object.

public int getAge(Service s)
{
    int age = s.execute(empId); // line 2
    return age; // line 3
}

Your unit test should look something like the following pseudo-code:

public int getAgeMinimumValueTest()
{
    ClassUnderTest uut = new ClassUnderTest();
    MockService service = new MockService();
    service.age = 10;
    int age = uut.getAge(service);
    Assert(age == 10);
}
2
  • 1
    If we are mocking both input and output, then what is the point ? How do we mock the output in the first place ? Commented Oct 1, 2012 at 12:46
  • 1
    When unit testing a single method, you should create a unit test for each use case. That is, in each unit test you specify different input values to the method, and verify that the output is the correct value. If the method has a dependency on a web service, you mock out the web service so that it behaves as expected in that use case. For example you may write mock code for the web service to provide values that are considered inputs to the method. Commented Oct 1, 2012 at 13:05
3

Yes you should be mocking your service. Mocking is the practice of using mock objects to replace the real objects you would normally use at runtime. Most of the time you would want to use a mocking framework to easily and dynamically create mock objects.

If you use mock objects, you would probably write code that looked more like this:

public int getAge(ServiceInterface service)
{
    return service.execute(empId);
}

where service is the actual service you're going to use at runtime, but during a unit test it is a mock object with mock behavior. Notice the parameter for getAge is no longer a concrete class, but instead is an interface. This allows you to use any object as a parameter as long as it implements the interface, instead of just one specific class. Your unit test would then take the following steps:

  1. Create the mock object
  2. Tell it to return a certain age when execute is called
  3. Call getAge with the mock object as a parameter
  4. Verify getAge returns the correct age.

For a good list of Java mocking frameworks, check out this stackoverflow question.

EDIT

Don't comment out your code and replace it with a constant just for your unit tests. The problem with this is that you'll have to make sure the right thing is commented when running unit tests, and the right thing is commented when releasing to the customer. This would turn into a nightmare for long-term maintenance purposes, especially if you intend on writing more than a 2 unit tests that require mocks. Aside from that, your tests should be run against the finished product; if not, that means you're not testing the actual code that is being released, which negates the purpose of having tests in the first place.

1

Yes, it is. Your job is to demonstrate that your code does the right thing, given its environment. The external web services are part of the environment; it's not your job to test their proper functioning, it's the job of the people who write the services. The input/output mapping that the tests should check is the input/output to your code; if the code produces input for another collaborator to do its job, that's strictly incidental.

In fact, even after the web services are available it is probably a good idea to keep your unit tests using a mock environment rather than the real one. It makes the tests simpler, less fragile with respect to the expected system context, and probably faster as well.

1
  • "Probably a good idea"? I'd say essential. You can't guarantee reliable Unit Tests across system boundaries.
    – thehowler
    Commented May 17, 2014 at 9:45
1

To add on to emddudley's excellent answer the biggest gain you can get in mocking the service is being able to test what should happen the service doesn't function correctly. The test pseudo-code could look something like this:

public int AgeMinimumValue_LogsServiceError_Test()
{
    ClassUnderTest uut = new ClassUnderTest();
    MockService service = new MockService();
    service.Throws(new TimeoutException());

    MockLogger logger = new MockLogger();

    try {
        int age = uut.getAge(service, logger);
        Assert.Fail("Exception was not raised by the class under test");
    }
    catch (TimeoutException) {
        Assert(logger.LogError().WasCalled());
    }
}

And now your implementation has been edited with this new requirement

public int getAge(Service s, Logger l)
{
    try {
        int age = s.execute(empId);
        return age;
    }
    catch(Exception ex) {
        l.LogError(ex);
        throw;
    }
}

In other scenarios it is more likely that you would need to respond to more complicated responses. If the service provided credit card processing you would need to respond to Success, Service Unavailable, Expired Credit Card, Invalid Number, etc. By mocking the service you can ensure you respond to these scenarios in a way that is proper for your situation. In this case you must mock the input/output from the service and the feedback you get from knowing that the consuming code will work for all known outputs is indeed meaningful and valuable.

EDIT: I just noticed you want to be able to mock without modifying the existing method. To do this the locate("ageservice"); method would need to be changed to support mock objects in tests, and locating the real service once it is ready. This is a variation of the service locator patter which abstracts away the logic to retrieve the implementation of the service you are using. A quick version of that may look like this:

public Service locate(string serviceToLocate) {
    if(testEnvironment) // Test environment should be set externally
        return MockService(serviceToLocate);
    else
        return Service(serviceToLocate);
}

My recommendation however would be to move the service dependencies into the Constructors:

public int AgeMinimumValue_LogsServiceError_Test()
{
    MockService service = new MockService();
    service.Throws(new TimeoutException());

    MockLogger logger = new MockLogger();

    ClassUnderTest uut = new ClassUnderTest(service, logger);

    try {
        int age = uut.getAge();
        Assert.Fail("Exception was not raised by the class under test");
    }
    catch (TimeoutException) {
        Assert(logger.LogError().WasCalled());
    }
}

Now the getAge method no longer has the responsibility of looking up the service as it has been abstracted out of the class completely leaving an implementation similar to this:

public int getAge()
{
    try {
        // _service is a private field set by the constructor
        int age = _service.execute(empId); 
        return age;
    }
    catch(Exception ex) {
         // _logger is a private field set by the constructor
        _logger.LogError(ex);
        throw;
    }
}
0

Are mocking both input and ouput values meaningful?

No, but you're not mocking the input value. Your test is being run, and the mock service verifies that the right part of the contract is accessed. That you happen to return a particular value is irrelevant.

On the otherside, if your method does any logic at all, then the return value matters. Here you're mocking the inputs to the logic (the result of the web service) and testing the output.

0

Now, how can I write it without modifying the existing code ?

you can-t. But you can create an interface "IMyService" that you can program against that contains all webservice methods-signatures.

public int getAge()
{
        IMyService s = getService(); 
        int age = s.execute(empId);
         return age; // line 3

}

In Production mode getService(); will return a reference to a full functional webservice and in testmode an alternative implemention (or mock) that returns your fake data.

0

Mocking isn't about inputs or outputs its about replacing external dependencies. So yes it is appropriate to write unit tests and mock out the external web services.

Now for the bad news: depending on the language and tooling available to you you may not be able to mock out the external dependencies unless the code has been designed to allow you to. If this is the case then you will find your tests actually turn into little integration tests rather then pure unti tests.

On the plus side it looks like you are alowed to modify the code (else how are you commenting it out) pass in an interface to your service to allow it to be mocked out (or use some other form of dependency injection)

Finally, what you are wanting to test appears to be a pure wrapper around the external service, there is almost nothing to unit test here other than that you call the service. As this is fundamentally an interface most of the testing will have to be done later at a higher level (integration testing)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.