In Grails I have a service that I want to unit test. The service uses these imports:
import grails.converters.JSON
import grails.web.JSONBuilder
I want the service to do get data and convert it to JSON:
def tables = DataProfileStats.withCriteria {
projections {
distinct("tableName")
}
};
The helper method I wrote to build the JSON is:
public String buildNodeString(String nodeText)
{
def builder = new JSONBuilder();
JSON result = builder.build {
hello = "world"
};
return result.toString();
}
In the unit test I have to add #TestMixin(ControllerUnitTestMixin) so the JSON adapter is loaded. But I also have to add #TestMixin(DomainClassUnitTestMixin) so I can mock the database object.
Any ideas on how to have multiple #TestMixin or is this a design issue with me having a import grails.web.JSONBuilder in a service class? Otherwise, I have to use a JAVA/JSON library or put the JSON stuff in a controller.
This is what I want the test to look like:
#TestMixin(ControllerUnitTestMixin)
#TestMixin(DomainClassUnitTestMixin)
class JsTreeJSONGeneratorServiceTests {
void testSomething() {
DataProfileStats stats1 = new DataProfileStats();
stats1.tableName = "table";
mockDomain(DataProfileStats, stats1);
JsTreeJSONGeneratorService service = new JsTreeJSONGeneratorService();
String json = service.buildNodeString();
assert json != "";
}
}
I get a #TestMixin(ControllerUnitTestMixin)
#TestMixin(DomainClassUnitTestMixin)
class JsTreeJSONGeneratorServiceTests {
void testSomething() {
DataProfileStats stats1 = new DataProfileStats();
stats1.tableName = "table";
mockDomain(DataProfileStats, stats1);
JsTreeJSONGeneratorService service = new JsTreeJSONGeneratorService();
String json = service.buildNodeString();
assert json != "";
}
}
I get a #TestMixin(ControllerUnitTestMixin)
#TestMixin(DomainClassUnitTestMixin)
class JsTreeJSONGeneratorServiceTests {
void testSomething() {
DataProfileStats stats1 = new DataProfileStats();
stats1.tableName = "table";
mockDomain(DataProfileStats, stats1);
JsTreeJSONGeneratorService service = new JsTreeJSONGeneratorService();
String json = service.buildNodeString();
assert json != "";
}
}
I get a "Cannot specify duplicate annotation on the same member : grails.test.mixin.TestMixin" exception.
Thanks
Found it!
#TestMixin([GrailsUnitTestMixin, ControllerUnitTestMixin, DomainClassUnitTestMixin])
Apparently, this is due to a Grails bug. The problem with mixing in the ControllerUnitTextMixin, is that it also does (and/or potentially will do) a lot of logic unrelated or unhelpful to services, and is essentially a workaround rather than a fix. Scott's answer is definitely sparse and clean in the sense that no other changes are made, but given some of the lack of backwards compatibility with Grails 2.0, I would be concerned with future releases that may, say, force logic into the setUp() method that may break for services.
So for completeness, I am including another potential workaround taken directly from the JIRA, all credit to Ellery Crane:
package util.converters
import org.codehaus.groovy.grails.web.converters.configuration.ConvertersConfigurationHolder
import org.codehaus.groovy.grails.web.converters.configuration.ConverterConfiguration
import org.codehaus.groovy.grails.web.converters.configuration.DefaultConverterConfiguration
import org.codehaus.groovy.grails.web.converters.marshaller.ObjectMarshaller
import org.codehaus.groovy.grails.web.converters.Converter
import org.codehaus.groovy.grails.web.converters.configuration.ChainedConverterConfiguration
class JSON extends grails.converters.JSON{
public JSON(Object target) {
super(target)
}
#Override
protected ConverterConfiguration<grails.converters.JSON> initConfig() {
ConverterConfiguration config = super.initConfig()
if(config.getOrderedObjectMarshallers().size() == 0){
initDefaultMarshallers()
config = super.initConfig()
}
return config
}
private void initDefaultMarshallers(){
List<ObjectMarshaller<grails.converters.JSON>> marshallers = new ArrayList<ObjectMarshaller<grails.converters.JSON>>();
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.json.ArrayMarshaller());
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.json.ByteArrayMarshaller());
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.json.CollectionMarshaller());
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.json.MapMarshaller());
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.json.EnumMarshaller());
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.ProxyUnwrappingMarshaller<grails.converters.JSON>());
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.json.DateMarshaller());
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.json.ToStringBeanMarshaller());
boolean includeDomainVersion = true;
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.json.DomainClassMarshaller(includeDomainVersion));
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.json.GroovyBeanMarshaller());
marshallers.add(new org.codehaus.groovy.grails.web.converters.marshaller.json.GenericJavaBeanMarshaller());
DefaultConverterConfiguration<grails.converters.JSON> cfg = new DefaultConverterConfiguration<grails.converters.JSON>(marshallers);
cfg.setEncoding("UTF-8");
cfg.setCircularReferenceBehaviour(Converter.CircularReferenceBehaviour.DEFAULT)
cfg.setPrettyPrint(false);
ConvertersConfigurationHolder.setDefaultConfiguration(grails.converters.JSON.class, new ChainedConverterConfiguration<grails.converters.JSON>(cfg));
}
}
then
Just import util.converters.JSON instead of grails.converters.JSON, and everything else works seamlessly.
Related
When I try to test a spring cloud stream function based method, it always happens NullPointerException about InputDestination.
I have two questions:
It's hard for me to know how to write UT from the official doc. official test doc
Besides, how to write integration Test if test file has some dependencies. It seems create a new context and always has NoSuchBeanDefination error.
I have tried as flow, but the context can not find some dependency beans.
#Test
public void sampleTest() {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
TestChannelBinderConfiguration.getCompleteConfiguration(
MyTestConfiguration.class))
.run("--spring.cloud.function.definition=uppercase")) {
InputDestination source = context.getBean(InputDestination.class);
OutputDestination target = context.getBean(OutputDestination.class);
source.send(new GenericMessage<byte[]>("hello".getBytes()));
assertThat(target.receive().getPayload()).isEqualTo("HELLO".getBytes());
}
}
So I just want to write UT, but still have NPE.
Here is my code.
#Bean
public Function<Message<List<DemoBean>>, Message<DemoBean>> findFirstBean( ){
return message -> {
List<DemoBean> demoBeans = message.getPayload();
return MessageBuilder.withPayload(demoBeans.get( 0 )).build();
};
}
Here is my test.
#SpringBootTest
#ActiveProfiles(profiles = "local")
#Import({ TestChannelBinderConfiguration.class})
class FunctionDemoTest {
#Autowired
private InputDestination inputDestination;
#Autowired
private OutputDestination outputDestination;
private FunctionDemo functionDemo;
// some dependency need to mock
private DemoService demoService;
#BeforeEach
void setUp() {
demoService = Mockito.mock( DemoService.class );
functionDemo = new FunctionDemo( demoService);
}
#Test
public void findFirstBeanTest() {
DemoBean demoBean = new DemoBean();
demoBean.setName("Howard");
demoBean.setAge( 1 );
DemoBean demoBean1 = new DemoBean();
demoBean1.setName("Frank");
demoBean1.setAge( 2 );
List<DemoBean> demoBeanList = new ArrayList<>();
demoBeanList.add( demoBean );
demoBeanList.add( demoBean1 );
Message<List<DemoBean>> inputMessage = MessageBuilder.withPayload(demoBeanList).build();
inputDestination.send(inputMessage,"findFirstBean-in-0");
Assertions.assertNotNull( outputDestination.receive( 10000, "findFirstBean-out-0") );
}
}
Here is error:
java.lang.NullPointerException: while trying to invoke the method org.springframework.messaging.SubscribableChannel.send(org.springframework.messaging.Message) of a null object returned from org.springframework.cloud.stream.binder.test.InputDestination.getChannelByName(java.lang.String)
at org.springframework.cloud.stream.binder.test.InputDestination.send(InputDestination.java:89)
at com.successfactors.caf.listener.FunctionDemoTest.raePdrResultProcessor(FunctionDemoTest.java:82)
Well, I know the root cause of NPE.
Message<byte[]> receive(long timeout, String bindingName)
It seems should be destinationName instead of bindingName in source code.
Any other answers would be appreciated.
I'm having trouble reading other Stack Overflow posts so after a few hours I'm looking for help.
I have two methods that I want to test. And I'd like to test the second one using Mock, but having trouble figuring out what to do.
Here's the first method:
String readFileContents(Path filePath) {
StringBuilder fileContents = new StringBuilder()
BufferedReader br = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)
String line
while ((line = br.readLine()) != null) {
fileContents.append(line).append('\n')
}
fileContents
}
And I test it with
class CdmFileSpec extends Specification {
private CdmFile cdmFile
private static final String filePath = 'src/test/resources/cdm/test/cdmFileTestFile.txt'
void setup() {
cdmFile = new CdmFile()
}
void 'test noFileExists'() {
given:
Path notRealPath = Paths.get('src/test/resources/cdm//test/notreal.txt')
when:
String fileContents = cdmFile.readFileContents(notRealPath)
then:
thrown NoSuchFileException
}
void 'test readFileContents() reads file contents'() {
given:
Path testFilePath = Paths.get(filePath)
when:
String fileContents = cdmFile.readFileContents(testFilePath)
then:
fileContents.contains('hip hop horrayy\n\nhoooo\n\nheyyy\n\nhoooo')
}
}
This works as I've placed a real file in the filePath.
I'm wondering... how can I test the next method using Mock?
void eachLineInFileAsString(Path filePath,
#ClosureParams(value = SimpleType, options = ['java.lang.String'] )Closure applyLine) {
BufferedReader br = Files.newBufferedReader(filePath)
String line
while ((line = br.readLine()) != null) {
applyLine.call(line)
}
}
The problem with mocking in so many cases is that methods create their own dependencies instead of having them injected or calling a mockable service method creating them. I suggest you refactor your code just a little bit, extracting BufferedReader creation into a service method:
package de.scrum_master.stackoverflow.q56772468
import groovy.transform.stc.ClosureParams
import groovy.transform.stc.SimpleType
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
class CdmFile {
String readFileContents(Path filePath) {
StringBuilder fileContents = new StringBuilder()
BufferedReader br = createBufferedReader(filePath)
String line
while ((line = br.readLine()) != null) {
fileContents.append(line).append('\n')
}
fileContents
}
void eachLineInFileAsString(
Path filePath,
#ClosureParams(value = SimpleType, options = ['java.lang.String']) Closure applyLine
) {
BufferedReader br = createBufferedReader(filePath)
String line
while ((line = br.readLine()) != null) {
applyLine.call(line)
}
}
protected BufferedReader createBufferedReader(Path filePath) {
Files.newBufferedReader(filePath, StandardCharsets.UTF_8)
}
}
Now mocking is quite simple and you don't even need your test resource file anymore (only if you want to do an integration test without mocks):
package de.scrum_master.stackoverflow.q56772468
import spock.lang.Specification
import java.nio.charset.StandardCharsets
import java.nio.file.NoSuchFileException
import java.nio.file.Path
import java.nio.file.Paths
class CmdFileTest extends Specification {
private static final String filePath = 'mock/cdmTestFile.txt'
private static final String fileContent = """
I heard, that you're settled down
That you found a girl and you're, married now
I heard, that your dreams came true
I guess she gave you things
I didn't give to you
""".stripIndent()
private CdmFile cdmFile
void setup() {
cdmFile = Spy() {
createBufferedReader(Paths.get(filePath)) >> {
new BufferedReader(
new InputStreamReader(
new ByteArrayInputStream(
fileContent.getBytes(StandardCharsets.UTF_8)
)
)
)
}
}
}
def "non-existent file leads to exception"() {
given:
Path notRealPath = Paths.get('notreal.txt')
when:
cdmFile.readFileContents(notRealPath)
then:
thrown NoSuchFileException
}
def "read file contents into a string"() {
given:
Path testFilePath = Paths.get(filePath)
when:
String fileContents = cdmFile.readFileContents(testFilePath)
then:
fileContents.contains("your dreams came true\nI guess")
}
def "handle file content line by line"() {
given:
def result = []
def closure = { line -> result << line }
Path testFilePath = Paths.get(filePath)
when:
cdmFile.eachLineInFileAsString(testFilePath, closure)
then:
result == fileContent.split("\n")
}
}
Please note that I am using a Spy() here, i.e. leaving the original CdmFile object intact and just stubbing the service method createBufferedReader(..) when called with exactly parameter Paths.get(filePath). For other paths the original method is called, which is important for the non-existent file test or if you want to add tests involving real resource file loading like in your own example.
Whenever it is difficult to test a class or component, difficult to inject mocks or otherwise isolate the subject under test, that is a reason to refactor your application code for better testability. When done right also it should also result in better separation of concerns and better componentisation. If your tests become very sophisticated, contrived, brittle and hard to understand and maintain, that is usually a smell and you ought to refactor the application code instead.
There's no need for a Mock, as you can just use a locally defined Closure:
def "test the method"() {
given:
def result = []
def closure = { line -> result << line }
Path testFilePath = Paths.get(filePath)
when:
eachLineInFileAsString(testFilePath, closure)
then: // I'm guessing here
result == [
'line 1',
'line 2',
'line 3',
'line 4'
]
}
sorry if my question confusing cause this is the first time i make a question. if there is something that i can do to make it clearer, just tell me so i can improve the way i'm asking.
currently i'm trying to make web service from netbeans that can check some data from database. Because i'm newbie so i followed tutorial from
http://programmerguru.com/webservice-tutorial/how-to-create-java-webservice-in-netbeans/ to make the web service.
but when i trying to check the database with my usual way with mybattis, it keep in give me java.lang.nulpointerexception. when i try to debug it, it give me "variable information not available, source compiled without -g option" and throw me to InvocationTargetException.java
public InvocationTargetException(Throwable target) {
super((Throwable)null); // Disallow initCause
this.target = target;
}
here is the code for the web service
/*
* To change this license header, choose License Headers in Project
Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.bismillah.berkah;
import com.bismillah.berkah.dao.DummyDao;
import com.bismillah.berkah.daoImpl.DummyDaoImpl;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
/**
*
* #author Ari
*/
#WebService(serviceName = "Check4Update")
public class Check4Update {
public DummyDaoImpl ddi;
/**
* This is a sample web service operation
*
* #param InTerminalNumber
* #return
*/
#WebMethod(operationName = "CheckUpdate")
public String hello(#WebParam(name = "InTerminalNumber") String InTerminalNumber) {
String OutError = "";
String OutMessage = "";
if (InTerminalNumber == null) {
return "InTerminalNumber can't be null";
} else {
Map mapdao = new HashMap<String, Object>();
Map map = new HashMap<String, Object>();
map.put("InTerminalNumber", InTerminalNumber);
mapdao = ddi.check4UpdatePatch(map);
OutError = (String) mapdao.get("OutError");
OutMessage = (String) mapdao.get("OutMessage");
return "Error : " + OutError + ", with message : " + OutMessage;
}
}
public void setDdi(DummyDaoImpl ddi) {
this.ddi = ddi;
}
}
and here is the code mybattis impl
package com.bismillah.berkah.daoImpl;
import com.bismillah.berkah.Check4Update;
import com.bismillah.berkah.config.MyBatisConnectionFactory;
import com.bismillah.berkah.dao.*;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
/**
*
* #author Ari
*/
public class DummyDaoImpl
{
private SqlSessionFactory sqlSessionFactory;
public DummyDaoImpl()
{
sqlSessionFactory = MyBatisConnectionFactory.getSqlSessionFactory();
}
public Map check4UpdatePatch(Map map)
{
Check4Update c4u = new Check4Update();
c4u.setDdi(this);
SqlSession session = sqlSessionFactory.openSession();
try
{
session.selectOne("dummy.check4UpdatePatch", map);
} catch (Exception ex)
{
ex.toString();
} finally
{
session.close();
}
return map;
}
}
could you tell me how to fix it? so i can get the data? by the way i always get thrown at my web service, here exactly
mapdao = ddi.check4UpdatePatch(map);
once again sorry if my question confusing cause this is the first time i make a question. if there is something that i can do to make it clearer, just tell me so i can improve the way i'm asking.
sorry already found the problem, it is cause wrong configuration on mybatis.
i haven't change my string resource on MyBatisConnectionFactory, also the one at xml file.
maybe that's why i always get java lang nul.
actually when i trying to fix this, i make some interface class too, but still not working until i find my problem. if anyone have some problem like me maybe you can fix it with adding some interface so the impl will override the interface.
How can I override the test name that appears on the TestNG report? I want to override the name that appears in the middle column (currently shows as the method name). Is this even possible?
I tried to do it like this, but it didn't work.
public class EchApiTest1 extends TestBase {
...
#BeforeTest
public void setUp() {
restClient = new RestClientPost();
this.setTestName( "ech: XXXXXX" );
}
And, the base class:
import org.testng.ITest;
public class TestBase implements ITest {
String testName = "";
#Override
public String getTestName() {
return this.testName;
}
public void setTestName( String name ) {
this.testName = name;
}
}
NOTE: The above code does work when I am viewing the report detail in the Jenkins TestNG plugin report, which shows the overridden test name as a string called "Instance Name:" at the beginning of the Reporter log output. Why, in this case, WHY does a "setTestName()" method alter a string labeled "Instance Name" in the report?
One answer I found had a suggestion like this but I don't know how to pass an ITestResult arg to a AfterMethod method:
#AfterMethod
public void setResultTestName( ITestResult result ) {
try {
BaseTestMethod bm = (BaseTestMethod)result.getMethod();
Field f = bm.getClass().getSuperclass().getDeclaredField("m_methodName");
f.setAccessible(true);
f.set( bm, bm.getMethodName() + "." + your_customized_name );
} catch ( Exception ex ) {
Reporter.log( "ex" + ex.getMessage() );
}
Thoughts?
Please find following code for set custom name of testcase in TestNG reports.
Following features are available in this code.
Dynamic execution on same test-case in multiple time
Set custom test-case name for reports
Set parallel execution of multiple test-cases execution
import java.lang.reflect.Field;
import org.testng.ITest;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
import org.testng.internal.BaseTestMethod;
import com.test.data.ServiceProcessData;
public class ServiceTest implements ITest {
protected ServiceProcessData serviceProcessData;
protected String testCaseName = "";
#Test
public void executeServiceTest() {
System.out.println(this.serviceProcessData.toString());
}
#Factory(dataProvider = "processDataList")
public RiskServiceTest(ServiceProcessData serviceProcessData) {
this.serviceProcessData = serviceProcessData;
}
#DataProvider(name = "processDataList", parallel = true)
public static Object[] getProcessDataList() {
Object[] serviceProcessDataList = new Object[0];
//Set data in serviceProcessDataList
return serviceProcessDataList;
}
#Override
public String getTestName() {
this.testCaseName = "User custom testcase name";
// this.testCaseName = this.serviceProcessData.getTestCaseCustomName();
return this.testCaseName;
}
#AfterMethod(alwaysRun = true)
public void setResultTestName(ITestResult result) {
try {
BaseTestMethod baseTestMethod = (BaseTestMethod) result.getMethod();
Field f = baseTestMethod.getClass().getSuperclass().getDeclaredField("m_methodName");
f.setAccessible(true);
f.set(baseTestMethod, this.testCaseName);
} catch (Exception e) {
ErrorMessageHelper.getInstance().setErrorMessage(e);
Reporter.log("Exception : " + e.getMessage());
}
}}
Thanks
I found a "workaround" but I am hoping for a better answer. I want to be able to show this "test name" OR "instance name" value on the HTML report (not just within the Reporter.log output) and I am starting to think its not possible :
#Test(dataProvider = "restdata2")
public void testGetNameFromResponse( TestArguments testArgs ) {
this.setTestName( "ech: " + testArgs.getTestName() );
Reporter.log( getTestName() ); // this magic shows test name on report
....
With this workaround, the user can now identify which test it was by looking at the Reporter.log output but I still wish the name was more prominant.
I suspect the answer lies in writing a TestListenerAdapter that somehow overrides the ITestResult.getTestNameMethod() method? That is the holy grail I am looking for.
The ‘result’ object will automatically pass in the method setResultTestName( ITestResult result )
Make sure you put alwaysRun=true like the following when you have groups defined in your test class otherwise “AfterMethod” will not be excuted.
#AfterMethod (alwaysRun=true)
I have created a class and published it as web service. I have created a web method like this:
public void addNewRow(MyObject cob) {
MyAppModule myAppModule = new MyAppModule();
try {
ViewObjectImpl vo = myAppModule.getMyVewObject1();
================> vo object is now null
Row r = vo.createRow();
r.setAttribute("Param1", cob.getParam1());
r.setAttribute("Param2", cob.getParam2());
vo.executeQuery();
getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
}
}
As I have written in code, myAppModule.getMyVewObject1() returns a null object. I do not understand why! As far as I know AppModule has to initialize the object by itself when I call "getMyVewObject1()" but maybe I am wrong, or maybe this is not the way it should be for web methods. Has anyone ever faced this issue? Any help would be very appreciated.
You can check nice tutorial: Building and Using Web Services with JDeveloper
It gives you general idea about how you should build your webservices with ADF.
Another approach is when you need to call existing Application Module from some bean that doesn't have needed environment (servlet, etc), then you can initialize it like this:
String appModuleName = "org.my.package.name.model.AppModule";
String appModuleConfig = "AppModuleLocal";
ApplicationModule am = Configuration.createRootApplicationModule(appModuleName, appModuleConfig);
Don't forget to release it:
Configuration.releaseRootApplicationModule(am, true);
And why you shouldn't really do it like this.
And even more...
Better aproach is to get access to binding layer and do call from there.
Here is a nice article.
Per Our PM : If you don't use it in the context of an ADF application then the following code should be used (sample code is from a project I am involved in). Note the release of the AM at the end of the request
#WebService(serviceName = "LightViewerSoapService")
public class LightViewerSoapService {
private final String amDef = " oracle.demo.lightbox.model.viewer.soap.services.LightBoxViewerService";
private final String config = "LightBoxViewerServiceLocal";
LightBoxViewerServiceImpl service;
public LightViewerSoapService() {
super();
}
#WebMethod
public List<Presentations> getAllUserPresentations(#WebParam(name = "userId") Long userId){
ArrayList<Presentations> al = new ArrayList<Presentations>();
service = (LightBoxViewerServiceImpl)getApplicationModule(amDef,config);
ViewObject vo = service.findViewObject("UserOwnedPresentations");
VariableValueManager vm = vo.ensureVariableManager();
vm.setVariableValue("userIdVariable", userId.toString());
vo.applyViewCriteria(vo.getViewCriteriaManager().getViewCriteria("byUserIdViewCriteria"));
Row rw = vo.first();
if(rw != null){
Presentations p = createPresentationFromRow(rw);
al.add(p);
while(vo.hasNext()){
rw = vo.next();
p = createPresentationFromRow(rw);
al.add(p);
}
}
releaseAm((ApplicationModule)service);
return al;
}
Have a look here too:
http://www.youtube.com/watch?v=jDBd3JuroMQ