When writing unit tests in Grails 3.x, we have to mock domains. Here is the example code.
package com.example.service
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import spock.lang.Ignore
import spock.lang.Specification
#TestFor(SomeService)
#Mock([DomainA, DomainB])
class SomeServiceSpec extends Specification{
...
}
The problem is when a new domain is added, lets say DomainC and the unit tests are dependent upon DomainC, then those unit tests fails. We then have to manually have to add DomainC.
Is there a way to dynamically mock the domains?
#TestFor(SomeService)
#Mock([dynamically mock all domain objects here])
class SomeServiceSpec extends Specification{
...
}
Maybe this is what you need (from grails doc).
Alternatively you can also use the DomainClassUnitTestMixin directly
with the TestMixin annotation and then call the mockDomain method to
mock domains during your test:
#TestFor(BookController)
#TestMixin(DomainClassUnitTestMixin)
class BookControllerSpec extends Specification {
void setupSpec() {
mockDomain(Book)
}
...
Also mockDomains method exists for the list of domains, which you can retrieve by standard way from context.
Related
I am trying to mock a class and also to control the return value of one of the methods. In the MUT the class gets instantiated and I am finding that while the method calls succeed I can not find any way to control the return value.
All the examples I can find show mocking the class methods or mocking a single method with patch object.
I could monkeypatch the entire class and not use Mock, but it seems to me that there must be a way to do this using Mock and have the benefit of all the assert_called*
Here is the simplest example I can come up with that demonstrates the problem. In practice it would be the MUT that instantiates the class but the problem is the same.
from unitest.mock import Mock
mock_class = Mock()
mock_class.method = Mock(return_value=2)
print("Class", mock_class.method(None))
instance = mock_class()
print("Instance", instance.method())
Class 2
Instance <Mock name='mock().method()' id='140446103686160'>
Also tried
from unitest.mock import Mock
mock_class = Mock()
mock_class.method = lambda s: 2
print("Class", mock_class.method(None))
instance = mock_class()
print("Instance", instance.method())
and
from unitest.mock import Mock
mock_class = Mock(method = lambda s: 2)
print("Class", mock_class.method(None))
instance = mock_class()
print("Instance", instance.method())
Same results.
Is there a way to add a method to a Mock class so that it exists in any subsequent instance of the class, or at least control the return value of an instance method for a mocked class?
I'm trying to test a Room database in Android, written in Kotlin, locally; that is not on an emulator and without instrumented testing. As I understand it this is possible using the Roboelectric framework but I'm having some trouble with this :
How does one select the appropriate class for the RunWith(CLASS) decorator ?
I understand AndroidJUnit4 is necessary for instrumented tests, given that this is a local test should I be using JUnit4 instead or some other derivative from RoboElectric.
How should I correctly determine the context ?
Trying InstrumentationRegistry.getTargetContext() requires that I use InstrumentationRegistry which isn't available when testing locally or that test can not be imported when importing androidx.test.platform.app.InstrumentationRegistry or android.support.test.InstrumentationRegistry. Trying ApplicationProvider.getApplicationContext() claims the reference getApplicationContext can't be found. I'm also not sure where I should be importing either InstrumentationRegistry or ApplicationProvider from.
In general I'm finding it tricky to determine the appropriate libraries to use in Java; all the documentation appears very version specific and assumes you magically know where to import a given class from where often such a classes appear in more then one library. Then package imported through gradle also seems to be well related but does not explicitly match the package being imported. If you have any general tips on this I would be quite keen to hear it.
My code so far is as follows:
package com.manaikan.airvendor.AirTime
import android.content.Context
import android.arch.persistence.room.Room
import androidx.test.platform.app.InstrumentationRegistry
import com.manaikan.airvendor.airtime.AirVendorDatabase
import com.manaikan.airvendor.airtime.BundleQuery
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import java.io.IOException
// Roboelectric
import androidx.test.core.app.ApplicationProvider
#RunWith(JUnit4::class)
class BundleTest {
private lateinit var queryset : ENTITYDao
private lateinit var database : APPLICATIONDatabase
#Before
fun setup()
{
val context = ApplicationProvider.getApplicationContext<Context>()
database = Room.inMemoryDatabaseBuilder(context, APPLICATIONDatabase::class.java)
.allowMainThreadQueries()
.build()
queryset = database.bundleQuery()
}
#After
#Throws(IOException::class)
fun tearDown()
{
database.close()
}
#Test
#Throws(Exception::class)
fun review()
{
}
}
It seems to me you were almost there. The main thing to change is the runner, use: AndroidJUnit4, and maybe the version of the dependencies. Assuming your database and DAO are set up correctly, this should work (add import for your classes):
package com.manaikan.airvendor.AirTime
import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.io.IOException
#RunWith(AndroidJUnit4::class)
class BundleTest {
private lateinit var queryset : ChannelProgramsDao
private lateinit var database : ChannelProgramsDatabase
#Before
fun setup()
{
val context = ApplicationProvider.getApplicationContext<Context>()
database = Room.inMemoryDatabaseBuilder(context, APPLICATIONDatabase::class.java)
.allowMainThreadQueries()
.build()
queryset = database.bundleQuery()
}
#After
#Throws(IOException::class)
fun tearDown()
{
database.close()
}
#Test
#Throws(Exception::class)
fun review()
{
}
}
It worked for me with the following dependencies:
testImplementation "org.robolectric:robolectric:4.2"
testImplementation "androidx.test.ext:junit:4.12"
implementation "androidx.room:room-runtime:2.2.2"
implementation "androidx.room:room-ktx:2.2.2"
kapt "androidx.room:room-compiler:2.2.2"
testImplementation "androidx.room:room-testing:2.2.2"
I was wondering as to how to build up a world of Domain class objects to use in my unit tests. What´s the best approach?
Say this is my code, ServiceX:
List<Course> getAllCoursesByType(String type) {
List<Course> list = Course.findAllByType(type)
list
}
This is the test for ServiceX:
import grails.buildtestdata.mixin.Build
import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification
#TestFor(ServiceX)
class ServiceXSpec extends Specification {
void "get all open courses"() {
given:
String type = "open"
when:
def list = service.getAllCoursesByType(type)
then:
list.size() == 4
}
}
How can I "pre populate" the test-db (memory) sow that I actually have 4 such objects in the list?
Create integration test for this. See an example here.
This is a judgement call for when to test using a slow integration test. The key is to test your code, not the Grails/hibernate DB code.
Integration tests should not be needed for the bulk of the service testing. I do think you need an integration test for the object interactions in a running system with a real DB. I tend to do that in GUI tests using GEB. These tests usually cover basic end-to-end scenarios. That tests the server side and the GUI interaction with the server.
In the GUI/GEB tests, I don't test all permutations and edge conditions of the service. I do most of that edge testing in unit tests.
I have found that with Grails, if one simple DB action works in an integration test, then most other simple DB actions work. The Grails domain mocks for save(), delete(), etc simulate the 'real' DB actions fairly well. Note: they do operate on objects in memory, so it is not exactly the same.
I don't use Spock, but with JUnit I use this approach (still works with Grails 3):
#TestFor(ServiceX)
#Mock([Course])
class ServiceXTests {
}
#Test
void testXYZ() {
def course= new Course(course: 'ABC')
assert course.save()
. . .
}
This appears to be supported with Spock. I would assume this creation of domain records belongs in the Spock 'given' section. Also, see Grails Testing.
Another great resource is to search the Grails source on Github. I learned a lot from their examples.
It turns out you can add / override methods to the domain classes (for example), something like this:
import grails.buildtestdata.mixin.Build
import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification
import grails.test.mixin.Mock
#Mock([Course])
#TestFor(ServiceX)
class ServiceXSpec extends Specification {
void "get all open courses"() {
given:
String type = "open"
Course.metaclass.static.findAllByType = { String type -> [new Course()]}
when:
def list = service.getAllCoursesByType(type)
then:
list.size() == 1
}
}
I have a basic mapping defined in my UrlMappings.groovy If I run my app and go to /api/address/zip-code/12345 I get the show action to respond to the browsers request.
group("/api/address"){
"/zip-code"( resources: 'zipCode', includes: ['show'] )
}
I am trying to create a test to validate that my mapping works because I am going to create a ton of mappings here and I want them to backed by a test. This is my test.
package com.vega.foo
import com.vega.foo.address.ZipCodeController
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import spock.lang.Specification
import org.codehaus.groovy.grails.web.mapping.UrlMappings
#TestFor(UrlMappings)
#Mock(ZipCodeController)
class UrlMappingsSpec extends Specification {
void "test zip code mapping"() {
expect:
assertForwardUrlMapping("/api/address/zip-code/12345", controller: 'zipCode', action: "show")
}
}
When I try and run this test I get the following error.
Compilation error compiling [unit] tests: BUG! exception in phase
'instruction selection' in source unit
'C:\websites********\test\unit\com\vega\foo\UrlMappingsSpec.groovy'
unexpected NullpointerException
This is about as basic of a URL Mappings test as you can write. What am I missing here?
PR. This is how I was able to pass unit spec.
Changes are almost what I had in comments.
Adding $id to url mapping.
Remove import org.codehaus.groovy.grails.web.mapping.UrlMappings from test.
Specifying the controller and the action explicitly in UrlMapping.
I have several TestCase classes in my django application. On some of them, I mock out a function which calls external resources by decorating the class with #mock.patch, which works great. One TestCase in my test suite, let's call it B(), depends on that external resource so I don't want it mocked out and I don't add the decorator. It looks something like this:
#mock.patch("myapp.external_resource_function", new=mock.MagicMock)
class A(TestCase):
# tests here
class B(TestBase):
# tests here which depend on external_resource_function
When I test B independently, things work as expected. However, when I run both tests together, A runs first but the function is still mocked out in B. How can I unmock that call? I've tried reloading the module, but it didn't help.
Patch has start and stop methods. Based on what I can see from the code you have provided, I would remove the decorator and use the setUp and tearDown methods found in the link in your classes.
class A(TestCase):
def setUp(self):
self.patcher1 = patch('myapp.external_resource_function', new=mock.MagicMock)
self.MockClass1 = self.patcher1.start()
def tearDown(self):
self.patcher1.stop()
def test_something(self):
...
>>> A('test_something').run()
Great answer. With regard to Ethereal's question, patch objects are pretty flexible in their use.
Here's one way to approach tests that require different patches. You could still use setUp and tearDown, but not to do the patch.start/stop bit.
You start() the patches in each test and you use a finally clause to make sure they get stopped().
Patches also support Context Manager stuff so that's another option, not shown here.
class A(TestCase):
patcher1 = patch('myapp.external_resource_function', new=mock.MagicMock)
patcher2 = patch('myapp.something_else', new=mock.MagicMock)
def test_something(self):
li_patcher = [self.patcher1]
for patcher in li_patcher:
patcher.start()
try:
pass
finally:
for patcher in li_patcher:
patcher.stop()
def test_something_else(self):
li_patcher = [self.patcher1, self.patcher2]
for patcher in li_patcher:
patcher.start()
try:
pass
finally:
for patcher in li_patcher:
patcher.stop()