I have FlatSpec test classes which need to make use of a REST service for some fixture data. When running all the tests in one go I only really want to instantiate the REST client once as it may be quite expensive. How can I go about this and can I also get it to work for running just one test class when I am running in my IDE?
1. Use mocking:
I would advice you to use some kind of mocking when you try to test REST service. You can try for example scala-mock. Creation of mock service isn't time/cpu consuming, so you can create mocks in all your tests and don't need to share them.
Look:
trait MyRestService {
def get(): String
}
class MyOtherService(val myRestService: MyRestService) {
def addParentheses = s"""(${myRestService.getClass()})"""
}
import org.scalamock.scalatest.MockFactory
class MySpec extends FreeSpec with MockFactory {
"test1 " in {
// create mock rest service and define it's behaviour
val myRestService = mock[MyRestService]
val myOtherService = new MyOtherService(myRestService)
inAnyOrder {
(myRestService.get _).expects().returning("ABC")
}
//now it's ready, you can use it
assert(myOtherService.addParentheses === "(ABC)")
}
}
2. Or use Sharing fixtures:
If you still want to use real implementation of you REST service and create only one instance and then share it with some test condider using:
get-fixture methods => Use it when you need the same mutable fixture objects in multiple tests, and don't need to clean up after.
withFixture(OneArgTest) => Use when all or most tests need the same fixtures that must be cleaned up afterwords.
Refer to http://www.scalatest.org/user_guide/sharing_fixtures#loanFixtureMethods for more details and code examples.
If you want to share the same fixture against multiple Suites use org.scalatest.Suites and #DoNotDiscover annotation (these requires at least scalatest-2.0.RC1)
Pawel's last comment fits well.
It was easier by inheriting from Suite with BeforaAndAfterAll instead of Suites.
import com.typesafe.config.ConfigFactory
import com.google.inject.Guice
import org.scalatest.{BeforeAndAfterAll, Suite}
import net.codingwell.scalaguice.InjectorExtensions.ScalaInjector
class EndToEndSuite extends Suite with BeforeAndAfterAll {
private val injector = {
val config = ConfigFactory.load
val module = new AppModule(config) // your module here
new ScalaInjector(Guice.createInjector(module))
}
override def afterAll {
// your shutdown if needed
}
override val nestedSuites = collection.immutable.IndexedSeq(
injector.instance[YourTest1],
injector.instance[YourTest2] //...
)
}
Related
We want to write tests for a project that uses Spring Boot and Spring AMQP. As we code in Kotlin we would like to use MockK instead of Mockito as it better fits Kotlin code style and best practices.
The RabbitListenerTestHarness class provides some convienient feature for testing #RabbitListeners. However, it returns implementations of Mockito's Answer interface, which are incompatible with the Answer interface of MockK.
Is there a way to use the Mockito answers with MockK, e.g. some exisiting wrappers for interoperability?
Consider the following example listener:
class SampleListener {
#RabbitListener(id = "sampleReceiver", queues = ["testQueue"])
fun receiveMessage(message: Message) {
}
}
and the actual test:
#SpringBootTest
class SampleTest(#Autowired val template: TestRabbitTemplate) {
#Autowired
lateinit var testHarness: RabbitListenerTestHarness
#Test
fun testRabbit() {
val spy = testHarness.getSpy<SampleListener>("sampleReceiver")
val answer: LatchCountDownAndCallRealMethodAnswer = testHarness.getLatchAnswerFor("sampleReceiver", 1)
// Mockito.doAnswer(answer).`when`(spy).receiveMessage(ArgumentMatchers.any())
every { spy.receiveMessage(any()) } answers { /* what goes here? */ }
template.convertAndSend("testQueue", "test")
}
}
The test contains the Mockito call, as mentioned in the Docs, as comment.
My question is, how can I use the answer object, returned from getLatchAnswerFor to complete the MockK stub?
It's probably easier to not use the harness at all and add your own proxy around the message listener.
get the container from the RabbitListenerEndpointRegistry
get listener from the container, wrap it in a proxy and set it on the container
stop/start the container
send message(s)
I have configured Spock Global Extension and static class ErrorListener inside it. Works fine for test errors when I want to catch feature title and errors if they happen. But how can I add some custom information to the listener?
For example I have test that calls some API. In case it fails I want to add request/response body to the listener (and report it later). Obviously I have request/response inside the feature or I can get it. How can I pass this information to the Listener and read later in the handling code?
package org.example
import groovy.json.JsonSlurper
import org.spockframework.runtime.AbstractRunListener
import org.spockframework.runtime.extension.AbstractGlobalExtension
import org.spockframework.runtime.model.ErrorInfo
import org.spockframework.runtime.model.IterationInfo
import org.spockframework.runtime.model.SpecInfo
import spock.lang.Specification
class OpenBrewerySpec extends Specification{
def getBreweryTest(){
def breweryText = new URL('https://api.openbrewerydb.org/breweries/1').text
def breweryJson = new JsonSlurper().parseText(breweryText)
//TODO catch breweryText for test result reporting if it is possible
expect:
breweryJson.country == 'United States'
}
def cleanup() {
specificationContext.currentSpec.listeners
.findAll { it instanceof TestResultExtension.ErrorListener }
.each {
def errorInfo = (it as TestResultExtension.ErrorListener).errorInfo
if (errorInfo)
println "Test failure in feature '${specificationContext.currentIteration.name}', " +
"exception class ${errorInfo.exception.class.simpleName}"
else
println "Test passed in feature '${specificationContext.currentIteration.name}'"
}
}
}
class TestResultExtension extends AbstractGlobalExtension {
#Override
void visitSpec(SpecInfo spec) {
spec.addListener(new ErrorListener())
}
static class ErrorListener extends AbstractRunListener {
ErrorInfo errorInfo
#Override
void beforeIteration(IterationInfo iteration) {
errorInfo = null
}
#Override
void error(ErrorInfo error) {
errorInfo = error
}
}
}
Create file src/test/resources/META-INF/services/org.spockframework.runtime.extension.IGlobalExtension
and place string "org.example.TestResultExtension" there to enable extension.
I am pretty sure you found my solution here. Then you also know that it is designed to know in a cleanup() methods if the test succeeded or failed because otherwise Spock does not make the information available. I do not understand why deliberately omitted that information and posted a fragment instead of the whole method or at least mentioned where your code snippet gets executed. That is not a helpful way of asking a question. Nobody would know except for me because I am the author of this global extension.
So now after having established that you are inside a cleanup() method, I can tell you: The information does not belong into the global extension because in the cleanup() method you have access to information from the test such as fields. Why don't you design your test in such a way that whatever information cleanup() needs it stored in a field as you would normally do without using any global extensions? The latter is only meant to help you establish the error status (passed vs. failed) as such.
BTW, I even doubt if you need additional information in the cleanup() method at all because its purpose it cleaning up, not reporting or logging anything. For that Spock has a reporting system which you can also write extensions for.
Sorry for not being more specific in my answer, but your question is equally unspecific. It is an instance of the XY problem, explaining how you think you should do something instead of explaining what you want to achieve. Your sample code omits important details, e.g. the core test code as such.
I have a class that serves as a model for some data I get from a server. This data starts as an unwieldy xml object where text nodes have attributes so the json format I convert it into does not have simple string values. Instead I have:
#Injectable()
export class FooString {
_attr: string;
value: string;
isReadOnly(): boolean {
return this._attr && this._attr === 'ReadOnly';
}
isHidden(): boolean {
return this._attr && this._attr === 'Hid';
}
}
Then my model is like:
#Injectable()
export class Payment {
constructor(
public FooId: FooString,
public FooStat: FooString,
public FooName: FooString ) { }
}
Everything ends up with the same instance of FooString. How do I get discrete instances for each of them?
I have tried a factory, but it still only creates a single instance:
export let fooStringProvider = provide(FooString, {
useFactory: (): FooString => {
console.log('in foostring factory');
return new FooString();
}
});
new FooString();
new Payment();
;-)
Why using DI when they don't have dependencies and you don't want to maintain single instances per provider. Therefore, just use new.
When to use DI
There are a few criterias when using DI instead of new the right thing:
If you want Angular to maintain and share instances
If you want to work with an interface or base class but then you want to configure from the outside what implementation should actually be used at runtime - like the MockBackend for Http during testing.
If you class has dependencies to instances and/or values provided by DI
If you want to be able to easily test classes in isolation (https://en.wikipedia.org/wiki/Inversion_of_control)
probably others ...
If there are good arguments to use DI, but you also want new instances then you can just provide a factory.
This answer https://stackoverflow.com/a/36046754/217408 contains a concrete example how to do that.
Using DI is usually a good idea. There are IMHO no strong arguments against using DI. Only when none of the above arguments apply and providing factories is too cumbersome, use new Xxx() instead.
How do I programmatically skip a test in the Spock framework? I know I can annotate a test with #Ignore to skip it, or use #IgnoreIf to skip tests based on environmental variables and the like. But is there a way to run arbitrary code that decides whether or not a test should run?
For example, let's say I have an integration test that has to connect to a third-party service's sandbox environment. Outages in the service's sandbox environment cause the test to fail. Assuming I've written a method canConnectToService that checks if the test will be able to connect to this service, how do I write a test that will be skipped if canConnectToService() returns false?
Use JUnit's Assume class. Specifically, you could write Assume.assumeTrue(canConnectToService()) at the beginning of your test to skip the test if the third party service is unavailable. This method will throw an AssumptionViolatedException if canConnectToService() returns false, and Spock ignores tests that are interrupted by an AssumptionViolatedException for JUnit compatibility (see this bug report).
There is another alternative (maybe it didn't exists before):
Using instance inside #Requires or #IgnoreIf:
Examples using inheritance, but not required:
abstract class BaseTest extends Specification {
abstract boolean serviceIsOnline()
#Requires({ instance.serviceIsOnline() })
def "some test" () { .. }
}
SubSpecification:
class OnlineTest extends BaseTest {
boolean serviceIsOnline() {
// Test connection, etc.
return true
}
}
class SkipTest extends BaseTest {
boolean serviceIsOnline() {
return false
}
}
Documentation
instance
The specification instance, if instance fields, shared
fields, or instance methods are needed. If this property is used, the
whole annotated element cannot be skipped up-front without executing
fixtures, data providers and similar. Instead, the whole workflow is
followed up to the feature method invocation, where then the closure
is checked, and it is decided whether to abort the specific iteration
or not.
As an extra, another way you can programmatically skip a test is using the where label:
class MyTest extends Specification {
List getAvailableServices() {
// You can test connections here or your conditions
// to enable testing or not.
return available
}
#Unroll
def "Testing something"() {
setup:
URL url = serviceUrl.toURL()
expect:
assert url.text.contains("Hello")
where:
serviceUrl << availableServices
}
}
Simple service class, AnalyzerService, calls stored proc in a database.
Attempting to run integration test to ensure service calls the stored proc and correct data is returned after analyzer class operates on it. However, getting the dreaded exception that "Cannot invoke method calculateEstimateNumberOfPositions() on null object". Why is the service object null? What am I missing?
THANK YOU!
package foobar.analyze
import static org.junit.Assert.*
import org.junit.*
import foobar.analyze.AnalyzerService
//#TestFor(AnalyzerService)
class AnalyzerServiceTests {
def AnalyzerService service
def dataSource
#Before
void setUp() { }
#After
void tearDown() { }
#Test
void testcalculateEstimateNumberOfPositions() {
String positionName = "crew"
String city = "Great Neck"
String state = "NY"
int numberOfPositionsSought = 100
int expectedNumberOfPositionsEstimate = 100
def numberOfPositionsEstimate = service.calculateEstimateNumberOfPositions(positionName, city, state, numberOfPositionsSought)
fail (numberOfPositionsEstimate != expectedNumberOfPositionsEstimate)
}
}
Convention. Stick to the convention. Anything out of convention, regarding nomenclature, will create problem during dependency injection.
Convention is to use the service class name as analyzerService instead of service in integration test.
The integration test should look like
class AnalyzerServiceTests extends GroovyTestCase {
//Service class injected only if you
//use the naming convention as below for AnalyzerService
def analyzerService
def dataSource
......
......
}
It was possible to use service in unit test case when you use the test mixin
#TestFor(AnalyzerService)
By using the above in unit test cases, you could use the default service variable in the test cases. This is not the same in case of integration tests.