Unit Testing and Static Methods

Patterns or practices for unit testing methods that call a static method

Using dependency injection (either option 2 or 4) is definitely my preferred method of attacking this. Not only does it make testing easier it helps to separate concerns and keep classes from getting bloated.

A clarification I need to make though is it is not true that static methods are hard to test. The problem with static methods occurs when they are used in another method. This makes the method that is calling the static method hard to test as the static method can not be mocked. The usual example of this is with I/O. In your example you are writing text to a file (WriteTextToFile). What if something should fail during this method? Since the method is static and it can't be mocked then you can't on demand create cases such as failure cases. If you create an interface then you can mock the call to WriteTextToFile and have it mock errors. Yes you'll have a few more interfaces and classes but normally you can group similar functions together logically in one class.

Without Dependency Injection:
This is pretty much option 1 where nothing is mocked. I don't see this as a solid strategy because it does not allow you to thoroughly test.

public void WriteMyFile(){
try{
using (FileStream fStream = File.Create(@"C:\test.txt")){
string text = MyUtilities.GetFormattedText("hello world");
MyUtilities.WriteTextToFile(text, fStream);
}
}
catch(Exception e){
//How do you test the code in here?
}
}

With Dependency Injection:

public void WriteMyFile(IFileRepository aRepository){
try{
using (FileStream fStream = aRepository.Create(@"C:\test.txt")){
string text = MyUtilities.GetFormattedText("hello world");
aRepository.WriteTextToFile(text, fStream);
}
}
catch(Exception e){
//You can now mock Create or WriteTextToFile and have it throw an exception to test this code.
}
}

On the flip side of this is do you want your business logic tests to fail if the file system/database can't be read/written to? If we're testing that the math is correct in our salary calculation we don't want IO errors to cause the test to fail.

Without Dependency Injection:

This is a bit of a strange example/method but I am only using it to illustrate my point.

public int GetNewSalary(int aRaiseAmount){
//Do you really want the test of this method to fail because the database couldn't be queried?
int oldSalary = DBUtilities.GetSalary();
return oldSalary + aRaiseAmount;
}

With Dependency Injection:

public int GetNewSalary(IDBRepository aRepository,int aRaiseAmount){
//This call can now be mocked to always return something.
int oldSalary = aRepository.GetSalary();
return oldSalary + aRaiseAmount;
}

Increased speed is an additional perk of mocking. IO is costly and reduction in IO will increase the speed of your tests. Not having to wait for a database transaction or file system function will improve your tests performance.

I've never used TypeMock so I can't speak much about it. My impression though is the same as yours that if you have to use it then there is probably some refactoring that could be done.

Unit Testing a static method

In Java, static methods are not designed to set dependencies.

So switching the dependency into a mock is really not natural.

You could provide a static setter for the field such as :

private static Codec codec;
public static void setCodec(Codec codec){
this.codec = codec;
}

And you could set a mock with setCodec(...) but ugh...

But forget, just do things well : refactor the code to remove all static and introduce a constructor that sets the codec.

private Codec codec;
public MyClassUnderTest(Codec codec){
this.codec codec;
}

IOC could help here to make the class under test a singleton and ease the dependency injections.

If not possible in your case, Java 5 enumeration could help you for at least the singleton concern.

unit testing and Static methods

Static methods themselves aren't harder to test than instance methods. The trouble arises when a method--static or otherwise--calls other static methods because you cannot isolate the method being tested. Here is a typical example method that can be difficult to test:

public function findUser($id) {
Assert::validIdentifier($id);
Log::debug("Looking for user $id"); // writes to a file
Database::connect(); // needs user, password, database info and a database
return Database::query(...); // needs a user table with data
}

What might you want to test with this method?

  • Passing anything other than a positive integer throws InvalidIdentifierException.
  • Database::query() receives the correct identifier.
  • A matching User is returned when found, null when not.

These requirements are simple, but you must also setup logging, connect to a database, load it with data, etc. The Database class should be solely responsible for testing that it can connect and query. The Log class should do the same for logging. findUser() should not have to deal with any of this, but it must because it depends on them.

If instead the method above made calls to instance methods on Database and Log instances, the test could pass in mock objects with scripted return values specific to the test at hand.

function testFindUserReturnsNullWhenNotFound() {
$log = $this->getMock('Log'); // ignore all logging calls
$database = $this->getMock('Database', array('connect', 'query');
$database->expects($this->once())->method('connect');
$database->expects($this->once())->method('query')
->with('<query string>', 5)
->will($this->returnValue(null));
$dao = new UserDao($log, $database);
self::assertNull($dao->findUser(5));
}

The above test will fail if findUser() neglects to call connect(), passes the wrong value for $id (5 above), or returns anything other than null. The beauty is that no database is involved, making the test quick and robust, meaning it won't fail for reasons unrelated to the test like network failure or bad sample data. It allows you to focus on what really matters: the functionality contained within findUser().

Unit Testing a Static Method of a Static Class

Refactor your class a little to take an identity as a parameter.

public static class ApplicationUtils
{
public static bool IsUserAManager(IIdentity identity)
{
if (identity == null)
throw new NullReferenceException("identity");

return identity.Name == "AdminUser";
}
}

And Your Test Class using Moq

[TestMethod]
public void IsUserAManagerTestIsAdminReturnsFalse()
{
var mockedIdentity = new Moq.Mock<IIdentity>();
mockedIdentity.Setup(x => x.Name).Returns("notanadmin");

var result = ApplicationUtils.IsUserAManager(mockedIdentity.Object);

Assert.IsFalse(result);
}

[TestMethod]
public void IsUserAManagerTestIsAdminReturnsTrue()
{
var mockedIdentity = new Moq.Mock<IIdentity>();
mockedIdentity.Setup(x => x.Name).Returns("AdminUser");

var result = ApplicationUtils.IsUserAManager(mockedIdentity.Object);

Assert.IsTrue(result);
}

JUnit test data in static or non static methods

The answer might not be related to "clean code", but rather be different depending on your current and future use cases.

Extending the fixture class in the First Way creates an important limitation - you won't be able to extend any other base class, and such case happens quite often in unit testing.

A static method in the Second Way can't be overridden.

You could use a Third Way, an interface with a default method. This won't limit you to a specific base class, and its default method may be overridden if you need this in the future:

public interface TestingData {
default String getHelloWorld() {
return "Hello World";
}
}

@RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest implements TestingData {
@Test
public void helloWorldTest() {
assertEquals("Hello World", getHelloWorld());
}
}

Check Unit testing in C: Best way to test static methods

It's debatable whether or not static functions should be tested, as they aren't part of the public API.

I test static functions by including the unit-under-test, rather than linking against it:

foo.c (Unit under test)

static int foo(int x)
{
return x;
}

/* ... */

test_foo.c

#include "foo.c"

void test_foo(void)
{
assert( foo(42) == 42 );
}

int main(void)
{
test_foo();
}

Compile just that test:

$ gcc -Wall -Werror -o test_foo test_foo.c
$ ./test_foo

Unit test private and static methods in java

All of the private methods in the class you're testing should be called by some public/protected/package private method; otherwise they're unused code. So, just concentrate on testing this public API that's visible to the "client code" of your app. The internals (private methods) will get tested/covered as a side effect because they actually implement the public contract that the API specifies.

Testing implementation details (private methods) directly would make the tests harder to maintain and the code-under-test more difficult to refactor.



Related Topics



Leave a reply



Submit