=================== ModelJUnit tutorial =================== .. highlight:: java In this tutorial, we implemented the model created last time (see :doc:`modeling-tutorial`) using ModelJUnit_. The example solution can be found on bitbucket_. The Model ========= First we implement the ``FsmModel`` interface to define our eFSM in ModelJUnit: For every input in our model, we are going to write a ModelJUnit ``Action``:: @Action public void myInput() { // Here we should update the current state... } and a guard that'll tell ModelJUnit when this action is available. The guard should be a method with the name ``Guard`` and return a boolean, e.g.:: public boolean myInputGuard() { return /* true if the current state allows myInput, false otherwise */ } Here is the model implemented during the tutorial .. literalinclude:: examples/MyModel.java :language: java The Adapter =========== Next we implemented the adapter that allows our model to communicate with and take control of our SUT. For each one of the ``@Action`` method in the model, we added a similarly named method in the adapter class:: class MyAdapter [ // ... public void myInput() { // Here we execute myInput on the SUT } // ... and, in the model, we make sure that each ``@Action`` calls the right adapter method:: class MyModel implements FsmModel { // ... @Action public void myInput() { // ... adapter.myInput(); } Here is the adapter created during the tutorial: .. literalinclude:: examples/MyAdapter.java :language: java Implementing reset ================== One of the methods that you need to implement as part of the ``FsmModel`` interface is ``reset``. In this method, your model is supposed to reset itself as well as reset the SUT to a known state. In case your SUT only store data in the browser, you can simply close the browser and open a new instance. But, in case your sut has a server-side data storage (e.g. a database) you will need to reset this to a known state as well. This might mean overriding files, clearing a mysql table or running a command. Implementing ``getState`` ========================= The second method that the ``FsmModel`` interface requires you to implement is ``getState``. This method should return an object, often a string, which uniquely identify the current state. Unlike what I did during the tutorial, do not include all the state variables in the returned stat, instead it should correspond to the *Visible States* of your model (the states in the bubbles). Generating tests ================ Once you have implemented your model, you can use it to generate tests. In the tutorial example, tests were generated in the main method:: public static void main(String[] argv) { MyModel model = new MyModel(); //Tester tester = new LookaheadTester(model); RandomTester tester = new RandomTester(model); //Tester tester = new AllRoundTester(model); //Tester tester = new GreedyTester(model); tester.buildGraph(); tester.addListener(new VerboseListener()); tester.addListener(new StopOnFailureListener()); tester.addCoverageMetric(new TransitionCoverage()); tester.addCoverageMetric(new StateCoverage()); tester.addCoverageMetric(new ActionCoverage()); tester.generate(20); tester.printCoverage(); } First, we need to instantiate your model:: MyModel model = new MyModel(); Then, we choose the test strategy. ModelJUnit offers four different strategies: AllRoundTester_, GreedyTester_, LookaheadTester_ and RandomTester_:: RandomTester tester = new RandomTester(model); Next we tell the tester to try and build the graph from the model implementation. This will basically generate a lot of tests until ModelJUnit thinks it has seen all the states. It'll be used later to compute the coverage metrics. (Hint: if you want the graph building to go faster, you can write your model so that it is possible to connect the adapter *after* you have run ``buildGraph``):: tester.buildGraph(); Then, we tell ModelJUnit to collect some information about the test run:: tester.addListener(new VerboseListener()); tester.addCoverageMetric(new TransitionCoverage()); tester.addCoverageMetric(new StateCoverage()); tester.addCoverageMetric(new ActionCoverage()); .. CAUTION:: There is a bug in ModelJUnit that prevent you to get correct state coverage with the ``AllRoundTester``. The coverage you get is only the number of covered states since the last reset. To get around this problem, use the following code to get state coverage:: tester.addCoverageMetric(new StateCoverage() { @Override public String getName() { return "Total state coverage"; } }); This tells ModelJUnit to stop the test on the first failure:: tester.addListener(new StopOnFailureListener()); Final, we generate a test sequence with a given length and print the collected metrics:: tester.generate(20); tester.printCoverage(); Usage with IntelliJ =================== There are some quirks to using ModelJUnit in IntelliJ, one way of integrating is found here: * :download:`IntelliJ ModelJUnit<../files/T04-MBT-Tutorial-files.zip>` .. _ModelJUnit: http://www.cs.waikato.ac.nz/~marku/mbt/modeljunit/ .. _bitbucket: https://bitbucket.org/modelbasedtesting/fire-mbt .. _here: https://bitbucket.org/modelbasedtesting/modeljunit-tutorial .. _AllRoundTester: _static/modeljunit2_docs/nz/ac/waikato/modeljunit/AllRoundTester.html .. _GreedyTester: _static/modeljunit2_docs/nz/ac/waikato/modeljunit/GreedyTester.html .. _LookaheadTester: _static/modeljunit2_docs/nz/ac/waikato/modeljunit/LookaheadTester.html .. _RandomTester: _static/modeljunit2_docs/nz/ac/waikato/modeljunit/RandomTester.html