开发者

Testable design

开发者 https://www.devze.com 2022-12-18 20:08 出处:网络
I have a java class which has a static member created using Facade (Singleton). Class A implements InterfaceA {

I have a java class which has a static member created using Facade (Singleton).

Class A implements InterfaceA {

 private static DataStore db = DataStoreFacade.getInstance("BDB"); //singleton instance

  public void save(final String key, final String val) {
     db.save(key,val);
  }
};

Here Class A is used as a member variable for webservice (stateless bean).

I can't test this code using EasyMock because there is no way to override the DataStore instance.

There are two options.

  1. Have a constructor taking the instance of DataStore which will set to db member variable.开发者_如何学JAVA The problem is I don't want webservice class to know which datastore instance has been created.

  2. Provide an additional protected Set Method to override the db object. This is what I have used where I create a Easy Mock object of DataStore and override the member variable. Is it the correct design.

What are the other possibilities?


You're right that is bad for testability. Use dependency injection and don't go for static variable:


public class A implements InterfaceA {

  private DataStore db;

  public A(DataStore db) {
    this.db = db;
  }

...

}

to inject or build either use dependency-injection framework (e.g. spring) or build the object somewhere in bootstrap factory code yourself.

production code:


new A(DataStoreFacade.getInstance("...");

test-code:


public void test_xxx(){
  DataStore db = EasyMock.createMock(DataStore.class);
  //... do some expectations and replay(db)
  InterfaceA a=new A(db);
  //...

}


Well, the original code is already testable. Here is a unit test for it, using JMockit:

@Test
public void testSave(final DataStore mockDb)
{
    final String key = "aKey";
    final String value = "aValue";

    new A().save(aKey, aValue);

    new Verifications()
    {{
        mockDb.save(key, value);
    }};
}

If needed, the DataStoreFacade class could be mocked too.


Why not make the db member protected, and in your test project inherit it and override that member:

project 
{
    Class A
    {
        protected static db = ...
        public void Save(...) { ... }
    }
}

test_project
{
    Class B : A
    {
        protected override static db = ... (create test db)
    }

    Class testB
    {
        public A a;

        public void Setup()
        {
            this.a = new B();
        }

        public void TearDown()
        {
            // delete a
        }

        public void TestSaveKey()
        {
            // test a
        }
    }
}

It's still hidden from consumers of the code/library, the test object isn't cluttering the production code, and the behaviour will be tested as if it were the production version.

Beware though, that having a static member for your db object could cause troubles for your tests if it's not cleaned up properly after each test.*

  • I know that you probably already know this, but I'm saying it for completeness.


Use the Supersede Instance pattern...

http://goodcoffeegoodcode.blogspot.com/2010/01/supercede-instance-pattern.html

0

精彩评论

暂无评论...
验证码 换一张
取 消