Mocking builders in JUnit test - java

Let's say if I have a test that uses builders to construct objects. The problem is that the builder() method in the builder class is static.
Generally, mocking a static method is already an indicator of bad design. However, in the case of builders, the builder() methods are always static. What's the best approach to unit testing methods using builders()? Should the builders be refactored into a separate class to facilitate mocking?
class Service {
private SqsClient sqsClient;
private String sqsQueueUrl;
public Service(String sqsQueueUrl) {
this.sqsClient = SqsClient.builder().build();
this.sqsQueueUrl = sqsQueueUrl;
}
public SqsClient getClient() {
return this.client;
}
public SqsClient setClient(SqsClient client) {
this.client = client;
}
public String getSqsQueueUrl() {
return this.sqsQueueUrl;
}
public void setSqsQueueUrl(String sqsQueueUrl) {
this.sqsQueueUrl = sqsQueueUrl;
}
public void onEvent(Activity activity) {
// How to mock builders in unit test?
DeleteMessageRequest deleteRequest = DeleteMessageRequest.builder().queueUrl(this.sqsQueueUrl).receiptHandle(activity.getReceiptHandle()).build();
DeleteMessageResponse deleteMessageResponse = this.sqsClient.deleteMessage(deleteRequest);
}
}
class ServiceTest {
#Test
public void testEvent() {
String sqsQueueUrl = "http://127.0.0.1";
String receiptHandle = "asdasd";
SqsClient sqsClient = EasyMock.mock(SqsClient.class);
Service service = EasyMock.mock(Service.class);
// EasyMock expect and replay here.
service.setClient(sqsClient);
service.setSqsQueueUrl(sqsQueueUrl);
Activity activity1 = new Activity();
activity.setReceiptHandle(receiptHandle);
service.onEvent(activity);
}
}

Related

Mock or override static method to test a class

I'm doing some unit test but I'm having problems trying to test a class. I have a class with a static builder method which returns the class instance:
public class MessageCaller {
public static MessageCaller builder() {
return new MessageCaller();
}
//Other methods
public String publish() {
//publishing to some Messages
return "something";
}
public MessageCaller withAttribute(String key, String value) {
//Some code
return this;
}
}
public class MessageCallerExtended extends MessageCaller {
private Map<String, String> attributes;
#Override
public MessageCaller withAttribute(String key, String value) {
if (this.attributes == null) {
this.attributes = new HashMap();
}
this.attributes.put(key, value);
return this;
}
//It's not working because it's calling the base class builder and is not possible to be Overriten
//because it's a static method.
public static MessageCallerExtended builder() {
return new MessageCallerExtended();
}
#Override
public String publish() {
return "test";
}
}
This is the method which I would like to test, the problem is that is calling the real publish method taking some time to finalize.
public void sendMessages(#Nonnull String group, #Nonnull String state) {
this.message.builder()
.toTopic(xxxx)
.withAttribute(xxx, xxx)
.withAttribute(xxx, xxx)
.withAttribute(xxx,xxx)
.publish();
}
I'm sending the message object in the constructor of the class.
I've created a Wrapper class to use in the unit test but the problem is that the builder method is static and for that reason is not possible to #Override, if I don't use the #Override tag I'll invoke the real builder method and then the real publish method and it is taking too much time to be processed, causing some problems, because is invoked for several unit test.
With Mockito I having similar issues with the static builder method, in fact it's not possible to mock static methods with Mockito. I'm not allowed to use another library like PowerMock for instance.
Any ideas?

How can I mock service with complex request in Mockito and JUint?

I have an interface:
public interface SenderService {
String send(long amount);
}
And I have an implementation of this interface:
public class SenderServiceAdapter implements SenderService {
private final ThirdPartyService thirdPartyService;
public SenderServiceAdapter(ThirdPartyService thirdPartyService) {
this.thirdPartyService = thirdPartyService;
}
#Override
public String send(long amount) {
ThirdPartyRequest thirdPartyRequest = new ThirdPartyRequest();
thirdPartyRequest.setAmount(amount);
thirdPartyRequest.setId(UUID.randomUUID().toString());
thirdPartyRequest.setDate(new Date());
ThirdPartyResponse thirdPartyResponse = thirdPartyService.send(thirdPartyRequest);
String status = thirdPartyResponse.getStatus();
if (status.equals("Error")) throw new RuntimeException("blablabla");
return thirdPartyResponse.getMessage();
}
}
Now I want to write Unit test for this service. I need to mock thirdPartyService's method send. But I don't understand how.
public class SenderServiceAdapterTest {
private ThirdPartyService thirdPartyService;
private SenderService senderService;
#Before
public void setUp() throws Exception {
thirdPartyService = Mockito.mock(ThirdPartyService.class);
senderService = new SenderServiceAdapter(thirdPartyService);
}
#Test
public void send() {
when(thirdPartyService.send(new ThirdPartyRequest())).thenReturn(new ThirdPartyResponse());
String message = senderService.send(100L);
}
}
The ThirdPartyRequest creates in SenderServiceAdapter. How can I mock it?
Try this:
doReturn(new ThirdPartyResponse()).when(thirdPartyService).send(any(ThirdPartyRequest.class));
Also by looking your code, you will need to set something in the response, so you will have to do this:
ThirdPartyResponse response = new ThirdPartyResponse(); //or mock
response.setStatus(...);
response.setMessage(...);
doReturn(response).when(thirdPartyService).send(any(ThirdPartyRequest.class));

How do I write this test case?

I am currently doing a small project on spring-mvc in my backend and I am trying to create tests for my converters and engines. Below is my BookmarkEngine.java file
#Service
public class BookmarkEngine implements IBookmarkEngine{
private static final String URL_PREFIX = "http://";
#Override
public String getFullUrl(String url) {
if(!url.startsWith(URL_PREFIX))
{
return URL_PREFIX + url;
}
return url;
}
}
How would I go about writing a test for this?
Here is my BookmarkEngineTest:
public class BookmarkEngineTest {
IBookmarkEngine bookmarkEngine = new BookmarkEngine();
private ViewBookmark defaultBookmark;
#Before
public void setUp() throws Exception {
defaultBookmark = new ViewBookmark();
defaultBookmark.setBookmarkId(1L);
defaultBookmark.setTitle("A sample bookmark");
defaultBookmark.setUrl("This is a sample bookmark.");
defaultBookmark.setAuthor(".");
defaultBookmark.setLastUpdated(1497812309081L);
}
#Test
public void getFullUrl() {
String result = bookmarkEngine.getFullUrl(defaultBookmark.getUrl());
assertThat(result.length(), is(defaultBookmark.getUrl().length()));
}
}
That certain test in getFullUrl() does not run, but how can I can make it work?
You can make a test to see if the strings will match such as
#Test
public void getFullUrl() {
String testurl = "facebook.com";
String testurl2 = "http://facebook.com";
assertEquals(bookmarkEngine.getFullUrl(testurl),"http://facebook.com");
assertEquals(bookmarkEngine.getFullUrl(testurl2),"http://facebook.com");
}

Mockito: Spying calls within the constructor (Java) [duplicate]

This question already has answers here:
Test class with a new() call in it with Mockito
(7 answers)
Closed 5 years ago.
This is not a duplicate of Test class with a new() call in it with Mockito. I'm trying to write a test to verify that certain methods are being called within the constructor of my spy object (mockToyFacade).
The class under test is ToyFactoryFacade. The idea is clients interact with the ToyFactoryFacade (which wraps a ToyFactory) to generate ToyFacades, which itself is a wrapper around the Toy object.
What I am trying to verify with Mockito?
I want to verify that addToyName(toyName) and addCreationTime(creationTimestamp) are being called on the ToyFacade. Both of these methods are called in the constructor of the ToyFacade.
What's the issue?
When I try to spy the ToyFacade, and verify that both aforementioned methods are called, I receive an error, which says "Actually, there were zero interactions with this mock." When I call the methods separately (i.e., not via the constructor), the verification check out correctly. I'm not sure what I'm doing incorrectly.
Test Code
public class ToyFactoryFacadeTest {
private Toy mockToy;
private ToyFacade mockToyFacade;
// System under test.
private ToyFactoryFacade toyFactoryFacade;
private ToyFactory mockToyFactory;
#Before
public void setup() {
mockToy = mock(Toy.class);
mockToyFacade = spy(new ToyFacade(mockToy, "Phone", System.currentTimeMillis()));
mockToyFactory = mock(ToyFactory.class);
toyFactoryFacade = new ToyFactoryFacade(mockToyFactory) {
#Override
public Toy getToyFacade(String toyName, long creationTimestamp){
return mockToyFacade;
}
};
}
#Test
public void testToyFactoryFacade() {
toyFactoryFacade.initializeAndGetToy("Phone", System.currentTimeMillis());
verify(mockToyFacade).addToyName("Phone");
verify(mockToyFacade).addCreationTime(anyLong());
}
}
Source Code
public class ToyFactoryFacade {
private final ToyFactory toyFactory;
public ToyFactoryFacade(ToyFactory toyFactory) {
this.toyFactory = toyFactory;
}
public ToyFacade initializeAndGetToy(String toyName, long creationTimestamp)
{
getToyFacade(toyName, creationTimestamp);
}
// For testing.
protected ToyFacade getToyFacade(String toyName, long creationTimestamp
{
return new ToyFacade(toyFactory.newToy(), toyName, creationTimestamp);
}
}
public class ToyFactory {
public Toy newToy() {
return new Toy();
}
}
public class ToyFacade {
private final Toy toy;
public ToyFacade(Toy toy, String toyName, long creationTimeStamp) {
this.toy = toy;
addToyName(toyName);
addCreationTime(creationTimestamp);
}
public void addToyName(String name) {
toy.addToyName(toyName);
}
public void addCreationTime(long timestamp) {
toy.addCreationTime(timestamp);
}
}
public class Toy {
public String toyName;
public String creationTimestamp;
public void addToyName(String name) {
toyName = name;
}
public void addCreationTime(long timestamp) {
creationTimestamp = timestamp;
}
}
Your test isn't doing what you expect because the method calls that you're trying to verify have already taken place before you create your spy. What you really want to do is to test the effect of those two method calls, rather than the calls themselves. This would look something like
verify(mockToy).addToyName("Phone");
verify(mockToy).addCreationTime(timestamp);
where timestamp is whatever you pass in in the setUp method.

Using non-static injected services in JUnit Parameterized Tests

I want to use Guice and GuiceBerry to inject a non-static legacy service into a factory class. I then want to inject that factory into my Parameterized JUnit test.
However, the issue is JUnit requires that the #Parameters method be static.
Example factory:
#Singleton
public class Ratings {
#Inject
private RatingService ratingService;
public Rating classicRating() {
return ratingService.getRatingById(1002)
}
// More rating factory methods
}
Example test usage:
#RunWith(Parameterized.class)
public class StaticInjectParamsTest {
#Rule
public GuiceBerryRule guiceBerryRule = new GuiceBerryRule(ExtendedTestMod.class)
#Inject
private static Ratings ratings;
#Parameter
public Rating rating;
#Parameters
public static Collection<Rating[]> ratingsParameters() {
return Arrays.asList(new Rating[][]{
{ratings.classicRating()}
// All the other ratings
});
}
#Test
public void shouldWork() {
//Use the rating in a test
}
}
I've tried requesting static injection for the factory method but the Parameters method gets called before the GuiceBerry #Rule. I've also considered using just the rating's Id as the parameters but I want to find a reusable solution. Maybe my approach is flawed?
Unfortunately, JUnit needs to be able to enumerate all of the tests before running any tests, so the parameters method must be called before rules.
You could define an enum for the type of rating:
#RunWith(Parameterized.class)
public class StaticInjectParamsTest {
#Rule
public GuiceBerryRule guiceBerryRule
= new GuiceBerryRule(ExtendedTestMod.class);
#Inject
private Ratings ratings;
#Parameter
public RatingType ratingType;
#Parameters
public static Collection<RatingType> types() {
return Arrays.asList(RatingType.values());
}
#Test
public void shouldWork() {
Rating rating = ratings.get(ratingType);
// Use the rating in a test
}
}
Edit: Code for enum:
public enum RatingType {
CLASSIC(1002),
COMPLEX(1020);
private final int ratingId;
private RatingType(int ratingId) {
this.ratingId = ratingId;
}
// option 1: keep rating ID private by having a method like this
public get(RatingService ratingService) {
return ratingService.getRatingById(ratingId);
}
// option 2: have a package-scope accessor
int getRatingId() {
return ratingId;
}
}
Edit: if you go with option 2 you would then add a new method to get a Rating from a RatingType which would delegate to the service passing ratingId:
#Singleton
public class Ratings {
#Inject
private RatingService ratingService;
public Rating getRating(RatingType ratingType) {
return ratingService.getRatingById(
ratingType.getRatingId());
}
// More rating factory methods
}
If you don't want RatingType to be in your public API, you can define it in your test, and have a method in the enum named getRating()
public enum RatingType {
CLASSIC {
#Override public Rating getRating(Ratings ratings) {
return ratings.getClassicRating();
}
},
COMPLEX {
#Override public Rating getRating(Ratings ratings) {
return ratings.getComplexRating();
}
};
public abstract Rating getRating(Ratings ratings);
}
You could also create a value type instead of an enum.
This assumes you can write tests that should pass for all Rating instances.
If you have some common tests but some rating-specific tests, I would make an abstract base class that contains common tests, and an abstract createRating() method, and subclass it for every rating type.
My solution was to add a RatingId class that wraps an integer and create a factory RatingIds that I could then return static and use as parameters. I overloaded the getRatingById method in my RatingService interface to accept the new RatingId type, and then inject the rating service into my test and use it directly.
Added factory:
public class RatingIds {
public static RatingId classic() {
return new RatingId(1002);
}
// Many more
}
Test:
#RunWith(Parameterized.class)
public class StaticInjectParamsTest {
#Rule
public GuiceBerryRule guiceBerryRule = new GuiceBerryRule(ExtendedTestMod.class)
#Inject
private RatingService ratingService
#Parameter
public RatingId ratingId;
#Parameters
public static Collection<RatingId[]> ratingsParameters() {
return Arrays.asList(new RatingId[][]{
{RatingIds.classic()}
// All the other ratings
});
}
#Test
public void shouldWork() {
Rating rating = ratingService.getRatingById(ratingId.getValue())
//Use the rating in a test
}
}
In cases as yours, where the total number of generated parameter sets is known in advance, but building the parameters itself requires some context (e.g. autowired service instance with Spring) you can go the functional approach (with junit5 & parameterized)
Obviously that does not work, if the createParameter function itself depends on such contex:-/
class MyTestClass {
// may be autowired, cannot be static but is required in parameter generation
SomeInstance instance;
private interface SomeParamBuilder { SomeParam build(SomeInstance i);}
private static Stream<Arguments> createParamterFactories() {
return Stream.of(
Arguments.of((SomeParamBuilder)(i)->
{
return new SomeParam(i);
})
);
}
// does not work, because SomeParam needs SomeInstance for construction
// which is not available in static context of createParameters.
//#ParameterizedTest(name = "[{index}] {0}")
//#MethodSource("createParameters")
//void myTest(SomeParam param) {
//}
#ParameterizedTest(name = "[{index}] {0}")
#MethodSource("createParamterFactories")
void myTest(SomeParamBuilder builder) {
SomeParam param = builder.build(instance);
// rest of your test code can use param.
}
}
maven dep:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
I did not get guiceberry to run (ancient dependencies), but using JUnitParamters and plain guice, this is rather simple:
#RunWith(JUnitParamsRunner.class)
public class GuiceJunitParamsTest {
public static class SquareService {
public int calculate(int num) {
return num * num;
}
}
#Inject
private SquareService squareService;
#Before
public void setUp() {
Guice.createInjector().injectMembers(this);
}
#Test
#Parameters({ "1,1", "2,4", "5,25" })
public void calculateSquares(int num, int result) throws Exception {
assertThat(squareService.calculate(num), is(result));
}
}
If you check the JUnitParams website, you will find a lot of other ways to define the parameters list. It is really easy to do this with the injecte service.

Categories

Resources