IntelliJ IDEA. Writing Tests with Spock (2021)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] in this screencast we're going to use the spock framework for creating and running unit tests let's start by creating a new project that's going to contain java project code and use spock to unit test that code from the new project window we can choose any of these three options to create our jvm project for this tutorial we're going to use gradle as our build tool spock is a groovy testing framework although we're going to use it to test java code so we could select groovy here since we know we're going to need it however we can also add groovy later on which is what i want to show so i'm not going to select it here we need a jdk i'm going to use jdk 11 for this project but there's no functionality here that requires 11 you can use whichever version you're comfortable with here let's call the project spock tutorial i'm going to save this to my default project directory intellij idea creates the files for the project and initializes the structure of the project it's using gradle to build the skeleton project to make sure all dependencies are downloaded and set up now the basics of the project have been created let's take a closer look and set up the dependencies we need intellij idea added junit 5 as our default testing framework which is a logical choice but we want to use spock instead of junit let's generate a new maven artifact dependency for the libraries we want i'm going to search for org.spock framework we want to add the latest version of the spock core library this is the milestone 4 release of spoc 2.0 for groovy3 this is a test dependency so let's change this declaration to test implementation we don't need any of the other junit dependencies now but we do need a dependency on groovy since spock is a groovy testing framework so we need groovy too we're going to pick the latest version of groovy3 here for now we're only going to use groovy for testing so we'll also declare this as test implementation 2. since we're using groovy we need to add the groovy plugin to gradle now we can load the gradle changes with shift command and i or ctrl shift o for windows and linux and intellij idea will download these new dependencies and correctly configure the project with a mixed language project like this one one way to organize the files is to keep java code in the java folder and to create a groovy folder for groovy code intellij idea knows about groovy source sets because we're using the groovy plugin in gradle so this directory name is suggested to us when we're using the groovy plugin we don't need to specifically add the java plugin as well since all that functionality is included in the groovy plugin we might want to delete this line just to keep the noise down in our build file we can keep the plugin to be explicit or delete it to be more succinct it's really down to our own preference let's start by writing a very simple test from the project window we can use the shortcut command in n or alt insert to create a new file we want to create a groovy class since spock tests are groovy classes we haven't created our package structure yet we can do this when we create the file itself by typing the full package before the class name spock tests are often called specifications so we'll call our test example specification we want this to extend spock's specification class we can get intellij idea to generate test methods for spock specifications this can be especially useful when we're not so familiar with writing spock or groovy code as intellij idea will generate the basic structure that we need the method is defined with the def keyword and spocs method names can be strings this is extremely useful when we're creating tests as the text format gives us a lot of flexibility for describing exactly what we're specifying the behavior should be spock tests don't use an annotation or a specific test method name format to define which methods are tests instead it uses labels we'll see the different types of labels available to use throughout the video for this simplest test we're going to use expect this can be used for defining the simplest behavior we expect to see spock also doesn't use assertions or an assert keyword at least not normally instead we can use simple checks like the double equals this method now specifies a simple expected behavior that the number one should be equal to one it's not a realistic test case but it shows the basic of a spot test we can run this test using the normal intellij idea shortcuts or the green arrow in the gutter near the line numbers we can see that this test passes which shouldn't be a surprise let's change it so that it fails it's always helpful to see a test fail first to make sure it's actually working and testing the right things when a test fails spock displays a helpful error message which shows exactly what failed and why intellij idea is using gradle to run these tests which is the default behavior for a gradle project this is normally what we want as it means our ide is using the same process to run tests as the continuous integration or build environment is using in a simple project like this one where the gradle build isn't doing anything extra like generating code or resources it can be faster to use the intellij idea test runner we can find this in the preferences under build execution deployment gradle and we can select intellij idea to run the tests let's fix this test and rerun it now it passes and we can see it's not using gradle to run it as we don't have any of the gradle output here one of the most powerful features of spock is how descriptive it is it's not just for testing code it's for describing and documenting expected behavior in our last test we use the expect label to indicate a simple expectation my personal favorite set of labels is given when then let's show this in a new test method we use the given label to indicate that this part of the test method is setting up the conditions required for the test for this test i need a polygon with four sides polygon is actually a java class we can use spock to test java or groovy code next we want to say when this is the thing we're actually testing the behavior we're trying to check in this test i want to make sure the number of sides has been correctly set finally we say then in this section we're going to check all the conditions we expect have been met in this test i want to check the number of sizes for the original number of sides i passed into the constructor this test now reads like given that i have a polygon with four sides when we get the number of sides then that number of sides should be equal to four let's run it and see if it passes it does which probably means the polygon behaves the way we expect it to assuming we wrote the test correctly using these labels might look like it's doubling the number of lines of code for a test method but remember that tests often have many more lines of code in them and grouping them in this way helps describe what's happening while we're here let's take a look at some things which might look odd if we're used to using java to test java code here we're actually reaching right inside the java class to inspect the value of a private field we can do this from groovy which can be helpful for testing private fields or methods without compromising their visibility in production code however intellij idea will give us a warning in case this is not something we want to do intentionally often if we need something visible for testing we probably will need it visible in production code too let's press alt and enter on the field and get intellij idea to create a getter for the field let's go back to the test now there's something odd about our call to number of sides the warning has gone and number of sides is no longer in bold it no longer looks like it's referencing a field in fact it's not if we hold down command or control and move our mouse over this we can see it's actually referencing the method get number of sides if we're calling a java getter from groovy code we can miss out the get at the start of the method name and groovy will still use the getter rather than the field this can be useful to reduce noise but you can still use the full method name with the get if you prefer it depends upon what you think is most readable sometimes removing the get might be confusing we've seen expect and we've seen given when and then but spock provides a lot of flexibility around which labels to use and when it's all about creating descriptive tests in our last test it did look like the labels might add a lot of noise for such a short test the setup for example is very simple and we could in line this into the actual test itself note that intellij id does support refactoring for groovy code now we've moved the setup code into the test we can remove the given label when we re-run the test it all still works as we expect we have the flexibility we need to label blocks of code in whichever way we think is most readable so the tests are documenting the expected behavior of the code we've seen that we could write run and refactor spock tests in intellij idea we can also install a plugin for extra support if we want we need to go to plugins type spock and search the plugin marketplace for spock plugins let's install this one it's written and supported by a third party it's not a jetbrains plugin it adds some helpful behavior like syntax highlighting for the labels and inspections to help us see if we're using the labels correctly using this plugin is entirely optional intellij idea provides all the support you need to write and run spock tests testing isn't just about testing the happy paths we should also check the exceptional cases often what we want to do is check that the right sort of exception gets thrown let's say we try to create a new polygon with zero sides this sounds like something that should cause an error let's use f2 to find out what the problem is with the when label this is the spock plugin telling us that we can't use this label on its own we need to give it a then block which asserts the correct state in our then block we want to check that the correct exception has been thrown we do this by calling throne with the class of the exception that we expect to be thrown in our case we expect a too few sides exception to have been thrown by the call to the constructor note that in java we'd normally have to add dot class onto the end of this to define that this is the type of the exception in groovy we don't need to do this but you can keep it if you think it makes the code clearer let's run this test to see if the behavior of the code is correct this test does pass this always makes me suspicious i like to see a test fail so i know it's really working let's change the expected exception to something that doesn't make sense let's change it to an array index out of bounds exception and rerun the test now the test fails and it tells us that it was expecting an array index out of bounds exception but it got a too few sides exception so now we know the code really is doing what we expect let's change the test back to be checking for the correct exception we can even assign the thrown exception to a variable so that we can perform some more checks on it let's check the number of sides stored on the too few sides exception to see if it matches the number of sides the polygon was created with when we run the test again we can see it's failing on this number of sides spock has a really helpful way of showing assertion failures it not only shows the assertion that failed but also breaks down the parts into the various values so we can see exactly where the issue is here we can see the number of sides stored on the exception is actually two when we were expecting zero let's go back into our polygon java class to see where this problem might come from here we can see our too few sides exception was initialized with a hard coded value of two when in fact it should have been sent the number of sides the polygon was created with let's rerun our test now i'm going to use find action shift command on a or control shift and a to re-run the test this time the test passes when we're testing a particular path we sometimes want to check that a known set of values leads to the same result this exception test is a really good example we know there's more than one input which should cause this exception to be thrown and we might want to test all of them in our case any integer that is less than 2 should cause the exception if we're using tests to document the expected behavior we might want to add the full list of values that can cause the exception or at least a sample list that demonstrates our expectations let's create a new test method that uses data pipes to do this for us i'm going to copy the previous test because this covers the basic test case and we're going to expand upon that in this new test we add another new label where which specifies the input values to the test for this test we want to test the constructor with more than one value so instead of passing in zero we'll pass in a variable sides our assertion also needs to check the number of sides on the exception matches the number of sides we passed into the constructor intellij idea is underlining this sides variable as it hasn't been defined or initialized anywhere we're going to do this in our wear block we define sides and then use the left shift operator to give a list of values that we want the int size value to use there's a few groovy things to note here firstly groovy supports operator overloading so the left shift operator is used here to show that this is the pipeline of values to use in the test secondly groovy has a friendly syntax for creating lists of values which is to simply put the values between square brackets see what happens when we run this test it's effectively run four different times the whole test is run once per value in that list intellij idea shows the name of the test then underneath the test name puts the value of sides for each of the four values all four of these runs passed because our code correctly throws the expected exception for each of these values if we want we can change the method name to make it easier to understand what's being tested we can use hash and the name of a data variable in the method name to create a true description when we rerun this intellij idea shows this updated method name with the value of sides and no extra noise look at what happens if one of these values causes the test to fail we know this exception should be thrown for a number of sides that's two or fewer so let's change one value to three when we run the test we can see one of the great things about data-driven testing all the tests are run even if one of the tests fails so we can see clearly which cases pass and which fail if one of them fails we can see what caused the problem in our case the test was expecting an exception to be thrown and it wasn't let's go back and fix the test data pipes aren't just for testing exceptional cases we might want to use them to test a series of valid inputs let's create another test once again we're going to create a polygon with a specified number of sides then we're going to check that the number of sides is the value we expect we're going to set up the sides variable with a whole list of valid values for the number of sides when we run this test we see something similar to the previous test we see a passing test for each one of the inputs for the value of sides once again if we change one of these to an invalid value we see this one case fail in our case because an exception was thrown and all the other cases pass let's fix the test and rerun it this test is quite a simple one so we could reduce the amount of code to do the same thing if we still think this is readable we can inline the creation of the polygon so we call the constructor in the same line as the assertion if we just have one statement which is set up test and assertion we can use the expect label like we did in our very first simple assertion test of course we still need the where block as this sets all the expected values for the number of sites data pipes are a nice way to specify a limited set of data to test spock also supports data tables for more complex data-driven testing as we've seen it's not unusual to want to pass in a series of values to check the same condition applies to all of them often we may have multiple inputs and want to check them against multiple outputs let's say we want to check the calculation of something like the maximum of two values a and b we'll want to check that the return is the expected maximum value we use the where to define the inputs to the test this time we can lay out a table of values the first line is the header the names of the variables we're going to use in the test separated by a pipe then we add a line for each set of inputs to the test so this first set of inputs might be 1 for a 3 for b then we expect the maximum of these two values to be three then we'll check a case where the value of a is the maximum value then a case where the values are equal when we run this test we see something similar to the data pipes tests we see a passing test for each of the rows in the data table described with the method name and the values for each of the input variables if we make one of these fail again we can see all the cases are run and we can see one of them fails spock's power assertions show the results of calculations all the input values and the comparison that failed we can use this to fix the problem we can make these data tables more readable by creating a separator between the columns let's use intellij ideas clone carrot feature by pressing option twice keeping it held down on the second press and pressing the down arrow to create a second carrot underneath the first on windows this is done by pressing the control key twice and keeping it held down on that second press now if we type a second pipe it appears on all the lines this pipe doesn't change the meaning of the test it simply helps us to understand the table better in our case we've grouped the expected output on one side and the inputs on the other running the test shows it all working as expected as before we can make the test a bit clearer by adding the names of the data variables into the test name now when we look at the test run it's really clear what's being tested and what the expected result is testing real applications can be more complex than simple calculation tests like these sometimes we need to mock out classes or apis to assert the expected behavior mocking is built into spock we don't need a separate library or framework for mock support it's also possible to mock concrete classes if you're used to using other mocking frameworks you might expect to only be able to mock java interfaces but spock lets us easily create a mock from a concrete class the given block of a spock test is the perfect place to set up mocks for our tests it's clearer then that this is all code that's required to run the test but is not the code that's being tested itself let's assume we want to mock a renderer class which is a concrete java class we can do this either by declaring a variable with type renderer and calling mock without any arguments or if we prefer to use groovy's def keyword to define our variables we'll need to pass the type in as an argument to the mock method bear in mind that if you declare it using def this variable is using groovy's dynamic typing and so isn't strongly recognized as a renderer type by the ide or by the code this is fine if you're not doing much with the mock but you might sometimes want to specify the type more clearly this will certainly be more natural for java developers now let's set up a polygon with a given renderer we'll call a polygon constructor that takes the number of sides and the renderer we'll pass the mocked renderer in here in our when section we'll define the call that's actually the thing we're testing let's say that in this test we want to see what happens when we call the draw method on this polygon we actually haven't created this method yet we're doing a bit of test driven development here we're writing the test first to define what we expect the behavior to be then we'll implement the code that makes that test pass the then block is where we're going to define the expectations spock has a nice clear syntax for defining the behavior we expect to see on the mock in this test we might expect to see four calls on the renderer's draw line method given that the polygon has four sides the then block states we expect to see render a dot draw line four times when tests get a little more complex sometimes it can be a bit unclear which object is being tested we can use spocs at subject annotation to document which object is specifically being tested this doesn't have any functional impact on the test it's just to mark the object we're testing let's use intellij idea to generate the missing methods just as empty methods enough to keep the compiler happy when we run this test now we'll see that it fails this is what we expect because the methods don't do anything yet we expected to see this draw line method called four times but it wasn't called at all now let's go into the implementation of the draw method if we make a change to call the renderer's drawline method in here and rerun the test we'll see the test still fails but this time it says the drawline method was called once we want this method to be called for all the sides in the polygon so we want this call to be inside some sort of loop we can create this loop a number of ways for example by using surround width and pressing the number 5 for a for loop or we can use a live template for example 4i will create a classic for loop with index we can use this to iterate for the number of sides and let's move the drawline call into this loop now when we rerun the test it passes the code is calling draw line on the renderer mock four times mocks are a powerful and useful tool to make sure that the code we're testing is calling the apis that we expect in the way we expect mocks are useful for checking calls out of our code stubs are useful for providing data or values into the code we're testing let's see an example of a stub in a new test method once again we're going to use the given block to set up the preconditions for the test method this time we're going to use the stub method to create a stub of the concrete pallet class like the mock you can define it this way or use def and pass the type into the stub method now we're going to set up the stub with the values it will produce when called by our code we use right shift to state that when the method get primary color is called the enum value red will be returned now let's initialize our renderer with this stub palette again we can mark this as the subject of our test if we want to i'm going to use an expect label because my test and the assertion are combined i expect that when i call get foreground color this will return color.red because i'm using this test to state that i expect get foreground color to return the same color as the palette's primary color once again we're using test driven development here we've used the test to drive out what we expect the methods to look like but they don't exist yet let's get intellij idea to create the most basic method for get foreground color that makes the code compile now let's run the test it fails of course because we haven't implemented get foreground color it just returns a null value however it's good to see the test fail first it usually indicates the test is checking the right thing even if that right thing hasn't been implemented yet let's go into the get foreground color method and get it to return the palette's primary color now when we re-run the test it passes the test injects a stub palette into the renderer we tell the stub palette what to return when the get primary color method is called so we can check that the renderer does what it's supposed to do when we call get foreground color if we had set this up as a mock instead of a stub this would have worked as well mock objects support the mocking behaviour we saw in the previous test and the stubbing behaviour we saw here whereas stub objects only support stubbing and not mocking my preference is to keep stub and mop behavior separate where possible so it's usually best to use stubs just for stubbing and mocks only for mocking we've now covered all the key features for creating and running tests with spock in the next few sections we'll look at additional features that can help you write short descriptive correct tests when tests get big we may want to split out large parts of code or common code into helper methods let's say we have a test like this one it uses a shape factory to create a default shape and then we perform a number of checks to make sure this meets our expectations you can imagine in real production code there might be a lot of values to check here not just the two we have here we may be tempted to move all these checks into their own method especially if they're going to be used by more than one test if we do this and rerun the test it will pass however let's check its passing for the right reason let's change an assertion so it should fail and it still passes this helper method is not doing what we expect if we move our assertions into a helper method like this it can no longer use the comparison operators to define the expected behavior instead we need to add the assert keyword specifically now when we rerun we should see the test fail there's something else to be aware of too it fails on the first assertion that fails it never runs the assertion to check the polygons renderer later in this video we'll look at how to address that for now let's make this test pass again this time it's passing for the correct reasons we can also accept intellij ideas suggestion that this method is static if we want let's look at an alternative approach to testing multiple properties of a single object let's reset the previous test back to how it was before we introduced the helper method we can use spocs with and a closure to check multiple values on the polygon inside this closure we don't have to say polygon dot we just assert the property matches the expected value let's change this so it fails so we can see how it works as with the helper method if the first assertion fails it doesn't run any further assertions this might be what you want from your test if one value is wrong the whole test should fail regardless but sometimes we want to run all the assertions so we can see exactly what's working and what's not let's look at how to make sure all our assertions are run regardless of whether one of them fails we can replace our with call with verify all instead let's run this and see what happens this time not only does the number of sides assertion fail but the check on the renderer also fails with verify all we will run all assertions and see which fail and which pass this can help us when we're iterating quickly between writing and fixing tests let's go back and fix this test and let's rename it so it correctly states what it's doing now note that with the string method names we can easily add quotes and other special characters let's look quickly at methods for setting up and tearing down data and state for our tests if you've used other testing frameworks the concept of a test or specification class setup and teardown will be familiar spock provides a setup method which we can generate with intellij idea which will be run before every individual test method in this class this can be used to set up a clean state at the start of each test to clean up data or state at the end of every test method you need a cleanup method this is run after every individual test method use the setup spec method to set up state once at the start of the specification this is for things that should not change between individual test methods create a cleanup spec method for final tear down code this method will be called once at the very end of running all the tests i like to move all my cleanup code to the end of the specification class since they're run at the end one final piece of useful information here the tests in this tutorial created the objects they were testing in the test methods themselves however you might also want to create your test instance as a field in the test you can use the at subject annotation on this field to show this is the object under test you can then reference this field in the test methods just as you'd expect to in any java or groovy class let's take a look at one last feature to help document the requirements via tests we've seen that spock has a focus on readability and tests as documentation the subject annotation the test labels strings as method names plus all you can do to customize these string values all contribute to being able to use the automated tests as documentation for what the system should do we can also add more information again for readability or documentation purposes to the blocks in our tests we can add a string value here that gives a more detailed description of this block if we want to split a block into further blocks for readability or to add documentation we can use the and label this is just to let us break things up further the text is available to the spoc runtime so these messages can be used in messages and reports spock is powerful and has even more to offer than we've looked at here believe it or not we've only touched on the surface of what spot can offer we've seen the basics of a test we've seen how to use labels to define tests we've seen the power of data driven testing if you want to find out more about spock take a look at the excellent reference documentation some of the examples you saw in this video came from there but there is also a lot more depth of information there's also a spock primer which is a great place to start with spock thanks for watching you
Info
Channel: IntelliJ IDEA by JetBrains
Views: 17,450
Rating: undefined out of 5
Keywords: spock, unit tests, intellij idea, java
Id: i5Qu3qYOfsM
Channel Id: undefined
Length: 36min 24sec (2184 seconds)
Published: Wed Jan 13 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.