Inject mock with Dagger for Espresso tests - java

I have tried modifying the code of this repo : Chiuki's android test
so it will fit Dagger's configuration for my project.
So What I did is :
1) Adding a Component for MainActivity :
#PerActivity
#Component(dependencies = DemoApplication.ApplicationComponent.class)
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
Clock getClock();
}
2) Modifying Application class to create and provide ApplicationComponent :
private ApplicationComponent component;
#Override
public void onCreate() {
super.onCreate();
component = createComponent();
component.inject(this);
}
protected ApplicationComponent createComponent() {
return DaggerDemoApplication_ApplicationComponent.builder().build();
}
public ApplicationComponent component() {
return component;
}
3) Perform injection in MainActivity's onCreate() :
DaggerMainActivityComponent.builder()
.applicationComponent(((DemoApplication) getApplication()).component())
.build()
.inject(this);
and I can verify by running the app that it seems to working.
And now I'm trying to modify accordingly tests in androidTest folder so
they will keep working.
First step was to change createComponent() method inside MockDemoApplication to provide the mock Clock instance :
#Override
protected ApplicationComponent createComponent() {
return DaggerDemoApplication_ApplicationComponent.builder()
.clockModule(new ClockModule() {
#Override
Clock provideClock() {
return Mockito.mock(Clock.class);
}
})
.build();
}
And second step was to perform the injection in MainActivityTest inside setUp() method :
#Before
public void setUp() {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
DemoApplication app
= (DemoApplication) instrumentation.getTargetContext().getApplicationContext();
DaggerMainActivityTest_TestComponent.builder()
.mockClockModule(new MockClockModule())
.build()
.inject(this);
}
So now I'm running the next test but it fails with AssertionFailedError :
Caused by: junit.framework.AssertionFailedError: 'with text: is "2008-09-23"' doesn't match the selected view.
Expected: with text: is "2008-09-23"
Got: "TextView{id=2131165184, res-name=date, visibility=VISIBLE, width=272, height=135, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=2017-11-22, input-type=0, ime-target=false, has-links=false}"
I'm guessing that the injection worked, since the test is not crashing with a NullPointerException when it calls clock instance, but somehow it is returning the

Related

Creating an annotation for JUnit 4/5 to initialize and inject an object in tests

I am developing a testing library for Kafka, Kafkaesque. The library lets you develop integration tests for Kafka using a fluid and elegant (?!) API. For now, I develop the version for Spring Kafka.
The library needs to be initialized in every test:
#Test
void consumeShouldConsumeMessagesProducesFromOutsideProducer() {
kafkaTemplate.sendDefault(1, "data1");
kafkaTemplate.sendDefault(2, "data2");
new SpringKafkaesque(broker)
.<Integer, String>consume()
.fromTopic(CONSUMER_TEST_TOPIC)
.waitingAtMost(1L, TimeUnit.SECONDS)
.waitingEmptyPolls(5, 100L, TimeUnit.MILLISECONDS)
.withDeserializers(new IntegerDeserializer(), new StringDeserializer())
.expecting()
.havingRecordsSize(2)
.assertingThatPayloads(Matchers.containsInAnyOrder("data1", "data2"))
.andCloseConsumer();
}
Instead of manually initializing the SpringKafkaesque object, I want to create an annotation that does the magic for me. Something like the #EmbeddedKafka annotation of Spring Kafka.
#SpringBootTest(classes = {TestConfiguration.class})
#Kafkaesque(
topics = {SpringKafkaesqueTest.CONSUMER_TEST_TOPIC, SpringKafkaesqueTest.PRODUCER_TEST_TOPIC})
class SpringKafkaesqueTest {
#Autowired
private Kafkaesque kafkaesque;
#Test
void consumeShouldConsumeMessagesProducesFromOutsideProducer() {
kafkaTemplate.sendDefault(1, "data1");
kafkaTemplate.sendDefault(2, "data2");
kafkaesque
.<Integer, String>consume()
.fromTopic(CONSUMER_TEST_TOPIC)
.waitingAtMost(1L, TimeUnit.SECONDS)
.waitingEmptyPolls(5, 100L, TimeUnit.MILLISECONDS)
.withDeserializers(new IntegerDeserializer(), new StringDeserializer())
.expecting()
.havingRecordsSize(2)
.assertingThatPayloads(Matchers.containsInAnyOrder("data1", "data2"))
.andCloseConsumer();
}
Is it possible? Any suggestion?
JUnit 4
One possible solution is to create a custom annotation processing using reflection. You can get the test method name with #Rule, so for example:
public class CustomAnnotationTest {
private SpringKafkaesque kafkaesqueInstance;
#Rule
public TestName testName = new TestName();
#Before
public void init() {
Method method = null;
try {
method = this.getClass().getMethod(testName.getMethodName());
} catch (Exception ex) {
// handle exceptions
}
if (method.isAnnotationPresent(EmbeddedKafka.class)) {
// Init your SpringKafkaesque instance here
// kafkaesqueInstance = new SpringKafkaesque(broker)
//
}
}
#EmbeddedKafka
#Test
public void testCustomAnnotated() {
// your test here
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#interface EmbeddedKafka {
}
}
You need to store this instance in the class-level variable. For the methods with no #EmbeddedKafka annotation, this variable will be null.
JUnit 5
With JUnit 5 you may consider using parameter injection with ParameterResolver. First of all, you need to implement this interface:
public class KafkaesqueResolver implements ParameterResolver {
#Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
return parameterContext.getParameter().getType() == SpringKafkaesque.class;
}
#Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
// Create an instance of SpringKafkaesque here and return it
return new SpringKafkaesque();
}
}
Next, add #ExtendWith(KafkaesqueResolver.class) annotation to your test class, and add a parameter to your test method, where you need the instance of SpringKafkaesque:
#ExtendWith(KafkaesqueResolver.class)
public class ParamInjectionTest {
#Test
public void testNoParams() {
// nothing to inject
}
#Test
public void testWithParam(SpringKafkaesque instance) {
// do what you need with your instance
}
}
No custom annotation required in this case.

self defined testng result listener

We defined one testng result listener which help us to send the testing result for each test case defined in testng.xml to one internal tool such like below:
public class TestResultsListener implements ITestListener, ISuiteListener {
#Override
public void onFinish(ISuite suite){
// some code to send the final suite result to internal tools
}
#Override
public void onTestSuccess(ITestResult iTestResult) {
this.sendResult(iTestResult,"PASS");Result
}
private void sendStatus(ITestResult iTestResult, String status){
// Set test case information
......
jsonArr.add(testResult);
}
}
And then we integrated this listener to other project's testng xml file such like:
<listeners>
<listener class-name="com.qa.test.listener.TestesultsListener" />
</listeners>
It worked as designed: once the test suite finishes, the test result will be uploaded to internal tools.
Now we have one requirement that in one project, one test case in testng.xml is related to 3 test cases in internal tool which means that for one test case in testng.xml we need to update 3 test cases in internal tools. How can we update our current testng listener to fulfill this?
Thanks a lot.
You can annotate each of your tests with the list of corresponding internal test tool ids:
Here I suppose that you have 2 testng tests: one is related to internal test IT-1, and the other one to internal tests IT-2, IT-3 and IT-4:
#Listeners(MyTestListener.class)
public class TestA {
#Test
#InternalTool(ids = "IT-1")
public void test1() {
System.out.println("test1");
fail();
}
#Test
#InternalTool(ids = {"IT-2", "IT-3", "IT-4"})
public void test2() {
System.out.println("test2");
}
}
The annotation is simply defined like this:
#Retention(RetentionPolicy.RUNTIME)
public #interface InternalTool {
String[] ids();
}
The your listener has just to figure out which annotation are present on successful/failed tests:
public class MyTestListener extends TestListenerAdapter implements ITestListener {
#Override
public void onTestSuccess(ITestResult tr) {
super.onTestSuccess(tr);
updateInternalTool(tr, true);
}
#Override
public void onTestFailure(ITestResult tr) {
super.onTestFailure(tr);
updateInternalTool(tr, false);
}
private void updateInternalTool(ITestResult tr, boolean success) {
InternalTool annotation = tr.getMethod().getConstructorOrMethod().getMethod().getAnnotation(InternalTool.class);
for (String id : annotation.ids()) {
System.out.println(String.format("Test with id [%s] is [%s]", id, success ? "successful" : "failed"));
}
}
}
The following output is produced:
test1
Test with id [IT-1] is [failed]
test2
Test with id [IT-2] is [successful]
Test with id [IT-3] is [successful]
Test with id [IT-4] is [successful]
You can also extend this mechanism to Suite listeners as well.
Disclaimer: The line
InternalTool annotation = tr.getMethod().getConstructorOrMethod().getMethod().getAnnotation(InternalTool.class); is not bullet-proof (high risk of null pointer exception). Should be more robust.

How can I get TestNG ITestResult in Spring test listener?

I'm writing tests using Spring and TestNG. An example of a test class:
#SpringBootTest
#TestExecutionListeners(
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS,
listeners = {TestListener.class}
)
public class BaseTest extends AbstractTestNGSpringContextTests
{
}
My TestListener class extends TransactionalTestExecutionListener so I have override methods for beforeTestClass(TestContext testContext), afterTestMethod(TestContext testContext) etc.
My problem is that within afterTestMethod I need to be able to get the TestNG ITestResult or TestResult so I can do different things depending on test success, fail or skip etc. Is there any way I can access ITestResult or TestResult from a spring test listener?
There is no easy direct way of getting access to the ITestResult object of a test method that was executed because Spring doesn't seem to provide access to it.
You can try doing something like this:
Build a utility method such that when given a Method object that represents a #Test method that was just executed, it would query the current ITestContext and find any ITestResult object whose Method object would match with the Method object that was provided.
Have your listener implementation query this utility method to get access to the ITestResult object.
Here's how a sample implementation could look like:
public class MyListener extends TransactionalTestExecutionListener {
#Override
public void afterTestMethod(TestContext testContext) throws Exception {
super.afterTestMethod(testContext);
ITestResult currentTestResult = getCorrespondingResultFor(testContext.getTestMethod());
}
private ITestResult getCorrespondingResultFor(Method method) {
ITestContext context = Reporter.getCurrentTestResult().getTestContext();
Set<ITestResult> allResults = new HashSet<>();
allResults.addAll(context.getPassedTests().getAllResults());
allResults.addAll(context.getFailedTests().getAllResults());
allResults.addAll(context.getSkippedTests().getAllResults());
return allResults
.stream()
.filter(result -> result.getMethod().getConstructorOrMethod().getMethod().equals(method))
.findAny()
.orElse(null);
}
}

Testing Controllers In Play Framework

I am using Play Framework and using Java as the language of choice. I have a Controller which makes a REST call to an external service. I intend to mock the external service, so that I can test the functionality of my controller. To achieve this, I have created my test cases as shown below (sample). I am embedding a server within my test to mock the external service.
public class SomeControllerTest extends WithApplication {
private static Server SERVER;
#Override
protected Application provideApplication() {
final Module testModule = new AbstractModule() {
#Override
public void configure() {
bind(AppDao.class).to(MockAppDaoImpl.class);
}
};
return new GuiceApplicationBuilder().in(Environment.simple()).overrides(testModule).build();
}
#BeforeClass
public static void setup() {
Router router = new RoutingDsl()
.POST("/api/users")
.routeTo(() -> created())
.build();
SERVER = Server.forRouter(router, 33373);
PORT = SERVER.httpPort();
}
#AfterClass
public static void tearDown() {
SERVER.stop();
}
#Test
public void testCreateUser() {
ObjectNode obj = Json.newObject();
obj.put("name", "John Doe");
obj.put("email", "john.doe#example.com");
Http.RequestBuilder request = new Http.RequestBuilder()
.method(POST)
.bodyJson(obj)
.uri("/some/url/here");
Result result = route(request);
assertEquals(ERR_MSG_STATUS_CODE, CREATED, result.status());
assertEquals(ERR_MSG_CONTENT_TYPE, Http.MimeTypes.JSON, result.contentType().get());
}
My expectation is that when I run the test, the mock server would run and based on my application's test configuration, my controller will make a call to the mock server which would return 201 and my test case would pass.
But, this doesn't happen, because as soon as setup() method completes, the mock server is killed, and my controller cannot make a call to it.
What am I doing wrong here?
Testing of controller should be rather done by inheritance from WithApplication
public class TestController extends WithApplication {
#Test
public void testSomething() {
Helpers.running(Helpers.fakeApplication(), () -> {
// put test stuff
// put asserts
});
}
}
In order to test a controller method use Helpers.fakeRequest and reverse routing.
The external service may be just mocked with mockito or other mocking framework you like.
You can find here several examples.

How to inject resource and use it in the constructor

I would like to inject resource and use it in the constructor of singleton class with roboguice injection. Below is an example that shows what I need but injected field is null in the constructor. I was reading something about providers and overthink another special class for getting url but I am not so sure if it's convenient solution. Code below:
#Singleton
public class ProductService implements IProductService {
#InjectResource(R.string.server_url)
private String serverBaseUrl;
IProductAPI productAPI;
public ProductService() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(serverBaseUrl)
.build();
productAPI = restAdapter.create(IProductAPI.class);
}
public ProductDTO retrieveProductByEan(String productEan) throws RetrofitError {
return productAPI.getProductByEan(productEan);
}
}
As I can see reading the Roboguice documentation, Roboguice framework is only thought for Android programming. See the following link.
Injections are done when the super.onCreate() method is called in an Activity.
Here is a portion of code of a full example in github:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // #Inject, #InjectResource, and #InjectExtra injection happens during super.onCreate()
sayText.setOnEditorActionListener(new OnEditorActionListener() {
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
// Have the remoteControl tell Astroboy to say something
remoteControl.say(textView.getText().toString());
textView.setText(null);
return true;
}
});
So I recommend to use a static value to refer serverBaseUrl.

Categories