How to create unit tests against non in-memory database such as MySQL in Play framework, with resetting to known state?

后端 未结 5 1939
梦谈多话
梦谈多话 2020-12-23 12:34

I want to create unit tests that cover code that use relational database in Play framework 2.1.0. There are many possibilities for this and all cause problems:

Test

相关标签:
5条回答
  • 2020-12-23 12:40

    Anybody used this approach (making evolutions compatible with both MySQL and H2)?

    I have found an answer for the MySQL specific features: How can I unit test for MySQL database with Play 2.x?

    0 讨论(0)
  • 2020-12-23 12:48

    When I wrote my tests for my postgres database I simply created a HashMap to connect to the database, and then I wrote test queries to make sure correct amount of records exists and so on... Here is my code.

        @Test
    public void testDataBase() {
        final HashMap<String,String> postgres = new HashMap<String, String>();
        postgres.put("db.default.driver","org.postgresql.Driver");
        postgres.put("db.default.url","jdbc:postgresql://localhost/myDataBase");
        postgres.put("db.default.user", "postgres");
        postgres.put("db.default.password", "password");
    
        running(fakeApplication(postgres), new Runnable() {
    
            @Override
            public void run() {
    
                //Insert Assertions Here
            }
        });
    }
    
    0 讨论(0)
  • 2020-12-23 12:49

    You can also using DB mock, if goal is to validate both your Slick|JPA|Anorm mappings & functions based on.

    When it's fit it has advantage to be more compliant with unit testing than a test DB, and more easy to manage (not setup/clear tasks, not sync of tests to avoid access to same test tables).

    You can have a look at my framework Acolyte ( http://github.com/cchantep/acolyte ) which is used in specs of Anorm itself (e.g. https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/SqlResultSpec.scala ).

    0 讨论(0)
  • 2020-12-23 12:57

    I used the same database engine that main database and dbunit for cleaning up before each test.

    public class SomeTest {
        // ...
    
        @Before
        public void startApp() throws Exception {
            // Set up connection to test database, different from main database. Config better should be used instead of hard-coding.
            Map<String, String> settings = new HashMap<String, String>();
            settings.put("db.default.url", "jdbc:mysql://localhost/somedatabase?characterEncoding=UTF-8&useOldAliasMetadataBehavior=true");
            settings.put("db.default.user", "root");
            settings.put("db.default.password", "root");
            settings.put("db.default.jndiName", "DefaultDS"); // make connection available to dbunit through JNDI
            app = Helpers.fakeApplication(settings);
            Helpers.start(app);
    
            databaseTester = new JndiDatabaseTester("DefaultDS");
    
            IDataSet initialDataSet = new FlatXmlDataSetBuilder().build(play.Play.application()
                    .resourceAsStream("/resources/dataset.xml"));
            databaseTester.setDataSet(initialDataSet);
            databaseTester.onSetup();
        }
    
        @After
        public void stopApp() throws Exception {
            databaseTester.onTearDown();
            Helpers.stop(app);
        }
    }
    

    My dataset.xml just contain table names to tell dbunit to empty these tables before each test. It also can contain fixtures.

    <?xml version="1.0" encoding="UTF-8"?>
    <dataset>
      <name_of_my_first_table />
      <name_of_my_second_table />
    </dataset>
    

    Evolutions run automatically on test database when using this approach, so if you remove all tables from test database, they will be recreated.

    It is overkill to use dbunit if you only need to clean tables, you can clean them by issuing query directly or by using ebean DdlGenerator. But I also use dbunit for comparing data.

    I don't use Helpers.running, because it takes Runnable and Runnable implementations cannot throw exceptions - very inconvenient for tests. But if you look at code for running(), it just calls Helpers.start() and Helpers.stop() so I call these methods directly in @Before and @After.

    Decided not to use H2 for running tests: yes, it runs faster, but there are too much difference between it and MySQL.

    0 讨论(0)
  • 2020-12-23 13:03

    First, I would recommend you to use the same RDBMS for testing and production as it could avoid some hard-to-find bugs.

    Concerning the need to clean your database between each test, you can use Ebean DdlGenerator to generate scripts to create a clean database and JUnit's @Before annotation to automatically execute these scripts before every test.

    Using the DdlGenerator can be done like this :

        EbeanServer server = Ebean.getServer(serverName);
        ServerConfig config = new ServerConfig();
        DdlGenerator ddl = new DdlGenerator((SpiEbeanServer) server, new MySqlPlatform(), config);
    

    This code can be placed in a base-class that you could make inherit your tests (or inside a custom Runner that you can use with the @RunWith annotation).

    It will also allow you to easily automate the FakeApplication creation, avoiding some boilerplate code.

    Some links that can be helpful :

    • http://blog.matthieuguillermin.fr/2012/03/unit-testing-tricks-for-play-2-0-and-ebean/
    • https://gist.github.com/nboire/2819920
    0 讨论(0)
提交回复
热议问题